Java Stream:forEach和forEachOrdered之间的区别

前提 :我已经阅读过这个问题和其他问题 ,但我需要一些澄清。

我知道Stream.forEach方法在处理并行流时会产生差异(不仅仅是这个),这就解释了为什么会这样

 //1 Stream.of("one ","two ","three ","four ","five ","six ") .parallel() .forEachOrdered(item -> System.out.print(item)); 

版画

 one two three four five six 

但是当涉及到中间操作时,当并行化流时,不再保证顺序。 所以这段代码

 //2 Stream.of("one ","two ","three ","four ","five ","six ") .parallel() .peek(item -> System.out.print(item)) .forEachOrdered(item -> System.out.print("")); 

会印出类似的东西

 four six five one three two 

forEachOrdered方法只影响其自身执行中元素的顺序是否正确? 直观地说,我想//1例子与…完全相同

 //3 Stream.of("one ","two ","three ","four ","five ","six ") .parallel() .peek(item -> System.out.print("")) //or any other intermediate operation .sequential() .forEach(item -> System.out.print(item)); 

我的直觉是错的吗? 我错过了整个机制的一些内容吗?

#3代码在概念上不等同于#1,因为parallel()sequential()调用会影响整个流,而不仅仅影响后续操作。 因此在#3情况下,整个过程将按顺序执行。 实际上#3案例类似于实际可以更改并行/顺序模式时Stream API的早期设计。 这被认为是不必要的复杂化(并且实际上添加了问题,例如,参见此讨论 ),因为通常您只需要更改模式以使终端操作有序(但不一定是顺序的)。 因此添加了forEachOrdered()并更改了parallel()/sequential()语义以影响整个流(请参阅此变更集 )。

基本上你是对的:在并行流中,中间操作没有订单保证。 如果需要按特定顺序执行它们,则必须使用顺序流。

你是对的,因为forEachOrdered的动作所做的保证只适用于那个动作,而不是其他任何东西。 但是假设这与.sequential().forEach(…)相同是.sequential().forEach(…)

sequential会将整个流管道转换为顺序模式,因此,传递给forEach的动作将由同一个线程执行,但也会执行前面的peek动作。 对于大多数中间操作, parallelsequential的确切位置是无关紧要的,并且指定两者都没有意义,因为只有最后一个是相关的。

此外,使用forEach时仍然没有对订购的保证,即使它在当前实现中没有任何后果。 这在“Stream.forEach是否遵守顺序流的遭遇顺序?”中讨论。

Stream.forEachOrdered的文档说明:

此操作一次一个地处理元素,如果存在则以遭遇顺序处理。 在为后续元素执行操作之前,对一个元素执行操作,但对于任何给定元素,可以在库选择的任何线程中执行操作。

因此,动作可能被不同的线程调用,如Thread.currentThread()可感知但不同时运行。 此外,如果流具有遭遇顺序 ,则它将在此处重构。 这个答案揭示了遭遇顺序处理顺序的差异。