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
动作。 对于大多数中间操作, parallel
或sequential
的确切位置是无关紧要的,并且指定两者都没有意义,因为只有最后一个是相关的。
此外,使用forEach
时仍然没有对订购的保证,即使它在当前实现中没有任何后果。 这在“Stream.forEach是否遵守顺序流的遭遇顺序?”中讨论。
Stream.forEachOrdered
的文档说明:
此操作一次一个地处理元素,如果存在则以遭遇顺序处理。 在为后续元素执行操作之前,对一个元素执行操作,但对于任何给定元素,可以在库选择的任何线程中执行操作。
因此,动作可能被不同的线程调用,如Thread.currentThread()
可感知但不同时运行。 此外,如果流具有遭遇顺序 ,则它将在此处重构。 这个答案揭示了遭遇顺序和处理顺序的差异。