为什么并行流在 Java 8 中按顺序收集

为什么以随机顺序打印数字,同时始终以原始顺序收集元素,即使从并行流中也是如此?forEachcollect

Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8};
List<Integer> listOfIntegers = new ArrayList<>(Arrays.asList(intArray));

System.out.println("Parallel Stream: ");
listOfIntegers
  .stream()
  .parallel()
  .forEach(e -> System.out.print(e + " "));
System.out.println();

// Collectors         
List<Integer> l = listOfIntegers
  .stream()
  .parallel()
  .collect(Collectors.toList());
System.out.println(l);

输出:

Parallel Stream: 
8 1 6 2 7 4 5 3 
[1, 2, 3, 4, 5, 6, 7, 8]

答案 1

这里有两种不同的“排序”,这使得讨论变得混乱。

一种是遭遇顺序,它在流文档中定义。考虑这一点的一个好方法是源集合中元素的空间顺序或从左到右的顺序。如果源是 ,则考虑较早的元素位于较后元素的左侧。List

还有处理时间顺序,这在文档中没有定义,但这是不同线程处理元素的时间顺序。如果列表的元素由不同的线程并行处理,则线程可能会在最左侧的元素之前处理列表中最右边的元素。但下一次可能不会。

即使并行完成计算,大多数和某些终端操作也会经过精心安排,以便它们保持从源到目标的遭遇顺序,而与不同线程可能处理每个元素的时间顺序无关。Collectors

请注意,终端操作不会保留遭遇顺序。相反,它由任何线程运行,以产生下一个结果。如果你想要类似的东西来保持遭遇顺序,请改用。forEachforEachforEachOrdered

另请参阅 Lambda 常见问题解答,进一步讨论排序问题。


答案 2

方法指定返回者按遭遇顺序将元素添加到列表中。Collector

返回:

一个收集器,它按遭遇顺序将所有输入元素收集到一个列表中

是否平行并不重要;顺序被保留。Stream

此外,查看源代码,返回的调用在合并时,这将保留顺序。例如,如果一个线程有 {1, 2},下一个线程有 {3, 4},则调用将产生 {1, 2, 3, 4}。此外,返回的不具有该特征。CollectorsCollectoraddAllArrayListaddAllCollectorUNORDERED


推荐