为什么并行流在 Java 8 中按顺序收集
Why parallel stream get collected sequentially in Java 8
为什么 forEach
以随机顺序打印数字,而 collect
总是按原始顺序收集元素,即使是并行流?
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]
Collectors.toList
method 指定返回的 Collector
将元素添加到列表中的顺序。
Returns:
a Collector which collects all the input elements into a List, in encounter order
Stream
是否平行并不重要;顺序被保留。
此外,查看 Collectors
源代码,返回的 Collector
在合并时调用 ArrayList
上的 addAll
,并且保留顺序。例如。如果一个线程有 {1, 2} 而下一个线程有 {3, 4},那么调用 addAll
会产生 {1, 2, 3, 4}。此外,返回的 Collector
没有 UNORDERED
特征。
这里有两种不同的 "ordering",这使讨论变得混乱。
一种是遇到命令,定义在streams documentation中。考虑这一点的一个好方法是源集合中元素的 spatial 或 left-to-right 顺序。如果源是 List
,请考虑较早的元素位于较晚的元素的左侧。
还有processing or temporal order,文档中没有定义,但是是时间顺序元素由不同的线程处理。如果列表的元素由不同的线程并行处理,则线程可能会先处理列表中最右边的元素,然后再处理最左边的元素。但是下一次可能就不会了。
即使计算是并行完成的,大多数 Collectors
和一些终端操作都经过精心安排,以便它们保留从源到目的地的 遇到顺序 ,与不同线程可能处理每个元素的 时间顺序 无关。
请注意,forEach
终端操作不会 保留遇到顺序。相反,它是 运行 由任何线程碰巧产生下一个结果。如果你想要像 forEach
这样保留相遇顺序的东西,请改用 forEachOrdered
。
另请参阅 Lambda FAQ 以进一步讨论排序问题。
为什么 forEach
以随机顺序打印数字,而 collect
总是按原始顺序收集元素,即使是并行流?
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]
Collectors.toList
method 指定返回的 Collector
将元素添加到列表中的顺序。
Returns:
a Collector which collects all the input elements into a List, in encounter order
Stream
是否平行并不重要;顺序被保留。
此外,查看 Collectors
源代码,返回的 Collector
在合并时调用 ArrayList
上的 addAll
,并且保留顺序。例如。如果一个线程有 {1, 2} 而下一个线程有 {3, 4},那么调用 addAll
会产生 {1, 2, 3, 4}。此外,返回的 Collector
没有 UNORDERED
特征。
这里有两种不同的 "ordering",这使讨论变得混乱。
一种是遇到命令,定义在streams documentation中。考虑这一点的一个好方法是源集合中元素的 spatial 或 left-to-right 顺序。如果源是 List
,请考虑较早的元素位于较晚的元素的左侧。
还有processing or temporal order,文档中没有定义,但是是时间顺序元素由不同的线程处理。如果列表的元素由不同的线程并行处理,则线程可能会先处理列表中最右边的元素,然后再处理最左边的元素。但是下一次可能就不会了。
即使计算是并行完成的,大多数 Collectors
和一些终端操作都经过精心安排,以便它们保留从源到目的地的 遇到顺序 ,与不同线程可能处理每个元素的 时间顺序 无关。
请注意,forEach
终端操作不会 保留遇到顺序。相反,它是 运行 由任何线程碰巧产生下一个结果。如果你想要像 forEach
这样保留相遇顺序的东西,请改用 forEachOrdered
。
另请参阅 Lambda FAQ 以进一步讨论排序问题。