如何获取一个Files.walk的并行流?

11

我需要对一个文件夹内的所有文件进行只读处理,且需要递归操作。我正在使用 Files.walk 获取文件流,但我注意到该API指定walk仅返回常规流而不是并行流。

如何才能并行处理目录中的所有文件?


4
像这样 Files.walk(...).parallel() 可能是一个选择。 - Flown
@Flown 哈哈...我太傻了。没意识到你可以将普通流转换为并行流。 - David says Reinstate Monica
2个回答

18

您可以通过调用Stream::parallel将任何Stream转换为并行Stream

Stream<Path> stream = Files.walk(startPath).parallel().forEach(...);

16
请注意,Files.walk 的并行化能力较差,特别是在子树下的文件数量少于1024个时。如果您需要对每个文件进行大量处理且文件数不多,使用Files.walk(path).collect(toList()).parallelStream()可能会更加高效。 - Tagir Valeev
1
@TagirValeev 有趣。你有一个链接可以解释为什么吗? - David says Reinstate Monica
8
@DavidGrinberg,JDK源代码和基准测试使用了内部的Spliterators.spliteratorUnknownSize,其分割策略是将块加载到包含1024个元素的数组中。由于大小未知,流水线引擎假定分割产生的部分相等,但实际上并非如此(对于<=1024输入的第一次分割,所有元素都倒入前缀中,使后缀中没有任何元素)。这导致非常糟糕的并行性能。 - Tagir Valeev
不起作用,对Files.walk中的流调用parallel并不能使其并行运行。JDK 1.8 - Jonathan Drapeau

7

我有同样的问题。调用 parallel() 将流转换为并行流后,Files.walk 流似乎不起作用。但实际上处理仍然只在一个线程中执行。

唯一的解决方案是将收集到的 Paths 转换成一个列表,并按照 Tagir Valeev 的建议创建一个并行流。

不可行的解决方案:

Files.walk(Paths.get(System.getProperty("user.dir")))
                    .parallel()
                    .filter(Files::isRegularFile)
                    ...

解决方案:

Files.walk(Paths.get(System.getProperty("user.dir")))
                    .collect(Collectors.toList())
                    .parallelStream()
                    .filter(Files::isRegularFile)
                    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接