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

为什么forEach以随机顺序打印数字,而collect总是以原始顺序收集元素,即使是从并行流中收集?

 Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8}; List 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 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] 

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

一种是遇到订单 ,它在流文档中定义。 考虑这一点的一个好方法是源集合中元素的空间从左到右的顺序。 如果源是List ,请考虑后面元素左侧的早期元素。

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

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

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

有关订购问题的进一步讨论,另请参阅Lambda FAQ 。

Collectors.toList方法指定返回的Collector按要求顺序将元素添加到列表中。

返回:

收集器,按照顺序将所有输入元素收集到List中

Stream是否平行并不重要; 订单被保留。

此外,查看Collectors源代码,返回的Collector在合并时在ArrayList上调用addAll ,并保留订单。 例如,如果一个线程有{1,2}且下一个线程有{3,4},那么对addAll的调用将产生{1,2,3,4}。 此外,返回的Collector器没有UNORDERED特性。