为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Java 8 流的映射函数来执行长时间运行的任务

使用 Java 8 流的映射函数来执行长时间运行的任务

哈士奇WWW 2024-01-05 14:55:33
我有一个方法,它将(远程文件的)URL 列表作为应该下载的参数。该方法返回一个其他类型的List(称为Attachment),它实际上包含java File类型的属性。对于本例,我使用 Java Stream API 迭代 URL 并在“map”函数中启动下载,该函数实际上返回 Attachment 实例。现在我的问题是:我是否滥用 Java Stream API 来做一些不该做的事情?喜欢将长时间运行的任务放入其中吗?我应该只对输入数据进行小操作吗?我现在看到的唯一缺点是测试有点困难。private List<Attachment> download(List<URL> attachments) {        return attachments.stream().map(attachmentUrl -> {            try {                Attachment attachment = new Attachment();                File attachmentFile = new File(getFilename(attachment.getAttachmentId(), attachmentUrl));                FileUtils.copyURLToFile(                        attachmentUrl,                        attachmentFile,                        CONNECT_TIMEOUT,                        READ_TIMEOUT);                attachment.setAttachmentFile(attachmentFile);                return attachment;            } catch (IOException e) {                e.printStackTrace();                LOGGER.error(e.getLocalizedMessage());            }            return null;        }).filter(Objects::nonNull).collect(Collectors.toList());    }
查看完整描述

2 回答

?
ABOUTYOU

TA贡献1812条经验 获得超5个赞

我认为考虑map其他函数结构(例如filter,reduce等)可能会有所帮助,而不是函数,而是语法。stream().map()是执行循环功能的语法for。问“我是否因为使用它执行的内容而滥用了该语法?” 那么就没那么有意义了:for循环不关心每次迭代运行的任务需要多长时间,也不关心map。它对其应用的操作是不可知的,因此唯一的问题是您是否正确使用语法,即循环遍历集合,从某物映射到某物。


在这种情况下,map语法在哪里,您想要的操作就完全没问题了。但是,您的实现可能会被清理一些。


attachmentUrl -> {

    try {

        Attachment attachment = new Attachment();

        File attachmentFile = new File(getFilename(attachment.getAttachmentId(), attachmentUrl));

        FileUtils.copyURLToFile(

                attachmentUrl,

                attachmentFile,

                CONNECT_TIMEOUT,

                READ_TIMEOUT);

        attachment.setAttachmentFile(attachmentFile);

        return attachment;

    } catch (IOException e) {

        e.printStackTrace();

        LOGGER.error(e.getLocalizedMessage());

    }

    return null;

}

对于内联 lambda 来说有点大map。一般来说,我倾向于对任何map需要大括号(即占用多行)的 lambda 持怀疑态度,尽管并不总是反对。我建议将此 lambda 重构为一个命名函数,并且可能是几个函数,它们要么是嵌套的(map(this::A)然后A调用B),要么是由流操作串行使用map(this::A).map(this::B)。


[编辑:] 关于并行化您的stream:请记住,作为此方法的一部分,您所做的不仅仅是 CPU 处理 - 您似乎正在执行网络 IO文件 IO。如果并行执行,您不仅会并行化 CPU 利用率,还会并行化网络和磁盘使用率。如果网络磁盘是主导因素,而不是 CPU,那么并行化可能不会给你带来什么好处,而且可能会让事情变得更糟。一般来说,更多的线程!=更快的网络或磁盘读/写。



查看完整回答
反对 回复 2024-01-05
?
森栏

TA贡献1810条经验 获得超5个赞

流是非常优雅的工具,可以以函数式编程方式处理数据,相同的输入将导致相同的输出,并且没有副作用,这将使您的代码不易出错且更具可读性。因此,无论输入量有多大,在使用方面都不存在滥用情况。如果您希望处理大量数据,则可以使用并行流。但是,您的实现可以进行一些清理,不要将所有业务逻辑委托给单个映射操作,使其更细粒度并将逻辑分散到多个映射器中,您可以像任何变量一样声明映射器,并将映射器插入Function<URL, File> urlToFileMapper = url -> {...}到溪流,attachments.stream().map(urlToFileMapper).map(anotherDeclaredMapper)...



查看完整回答
反对 回复 2024-01-05
  • 2 回答
  • 0 关注
  • 119 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信