有没有一种首选的方法将一系列列表收集成一个平面列表?

10

我在想,是否有一种首选的方法可以从列表流中获取包含所有列表元素的集合。 我能想到两种方法来实现这个目标:

final Stream<List<Integer>> stream = Stream.empty();
final List<Integer> one = stream.collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
final List<Integer> two = stream.flatMap(List::stream).collect(Collectors.toList());

在我看来,第二种选择更漂亮,但我猜第一种在并行流方面更有效率。 对于这两种方法,还有进一步的支持或反对的论点吗?


The second option looks much nicer to me, but I guess the first one is more efficient in parallel streams. Are there further arguments for or against one of the two methods?
2个回答

9
主要区别在于flatMap中间操作,而collect终端操作
因此,如果您想进行除collect之外的其他操作,则flatMap是处理平铺流项目的唯一方式
此外,collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll)很难阅读,因为您有两个具有完全不同语义的相同方法引用ArrayList::addAll
关于并行处理,你的猜测是错误的。第一个方法的并行处理能力较低,因为它依赖于应用于流项目(子列表)的ArrayList.addAll,而这些项目无法分解为并行子步骤。相比之下,Collectors.toList()应用于flatMap 可以对子列表项进行并行处理,如果在流中遇到支持并行处理的特定List。但只有当你有一个相当小的流和相当大的子列表时,这才会有用。 flatMap唯一的缺点是中间流的创建,如果你有很多非常小的子列表,在这种情况下会增加开销。
但在你的示例中,流为空,所以这不重要(scnr)。

您能否详细说明为什么第一个版本在并行流中表现不佳?在 collect 前面的中间操作可以并行执行,并且使用累加器函数(collect 的第二个参数)可以维护多个 ArrayList。然后,组合器(第三个参数)将用于将所有元素收集到一个大的 ArrayList 中。(在这种情况下,两个函数当然是相同的,但使用方式不同) - muued
1
并行处理在两个版本中的 Stream 处理方面是相同的,即 外部 流。但在第二个版本中,扁平化流由 子列表 的元素组成,这允许在子列表的中间分割操作(如果可行)。这在第一个版本中是不可能的。正如所说,只有在流包含一些相当大的子列表(而且它们的大小不同)时,这才是相关的。由于在 flatMapcollect 之间没有中间操作,因此只有 collect 本身受到影响,因此差异可能非常小。 - Holger
好的,如果在flatMapcollect之间需要进行操作,那么肯定不应该使用第一个版本。我没有考虑到这些。谢谢你的回复。 - muued

4

我认为选项二的意图比选项一清晰得多。对于第一个选项,我花了几秒钟才弄清楚发生了什么,它看起来不太“正确” - 尽管它似乎是有效的。选项二对我来说更明显。

本质上,您所做的事情的意图是一个flatmap。如果是这种情况,我希望看到使用flatmap而不是使用addAll()。


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