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

我有一个方法,它将(远程文件的)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());

    }


哈士奇WWW
浏览 120回答 2
2回答

ABOUTYOU

我认为考虑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,那么并行化可能不会给你带来什么好处,而且可能会让事情变得更糟。一般来说,更多的线程!=更快的网络或磁盘读/写。

森栏

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

相关分类

Java