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

Bar*_*jeu 33 java parallel-processing java-8 java-stream

为什么要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);
Run Code Online (Sandbox Code Playgroud)

输出:

Parallel Stream: 
8 1 6 2 7 4 5 3 
[1, 2, 3, 4, 5, 6, 7, 8]
Run Code Online (Sandbox Code Playgroud)

Stu*_*rks 42

这里有两种不同的"排序",这使讨论变得混乱.

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

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

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

需要注意的是,forEach终端的操作并不能保留相遇秩序.相反,它由任何产生下一个结果的线程运行.如果你想要这样的东西forEach保留遭遇订单,请forEachOrdered改用.

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


rge*_*man 10

Collectors.toList方法指定返回的Collector元素按遭遇顺序添加到列表中.

返回:

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

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

此外,看Collectors源代码,返回的Collector电话addAll上的ArrayList合并时,和保留的顺序.例如,如果一个线程有{1,2}且下一个线程有{3,4},那么调用addAll产生{1,2,3,4}.此外,返回Collector没有UNORDERED特征.

  • @BaratSahdzijeu它不会忽略`parallel`.在并行操作中,处理的*temporal*(时间)顺序可以并且将在从运行到运行的元素之间变化.但是对于像列表这样的源和目的地,从源到目的地保留"遭遇"顺序(空间或从左到右的顺序). (3认同)
  • @rgettman正确,`forEach`不保留遭遇顺序.如果需要保留遭遇顺序,请使用`forEachOrdered`. (3认同)
  • 是什么让你觉得"平行"将丢弃订单? (2认同)
  • @BaratSahdzijeu我不认为它忽略了`parallel`,它只是按顺序组合工作线程的结果.另一方面,你的`forEach`将在工作线程中自行打印元素; 这会破坏任何订单. (2认同)