Java Streams API中的"forEach"为什么是无序的?

4
据我所知,在并行流中,像 findFirstskiplimit 等方法只要流有序(默认情况下是有序的),无论并行或串行,它们的行为都保持不变。因此我想知道为什么 forEach 方法会有所不同。我思考了一下,但我就是无法理解为什么需要定义 forEachOrdered 方法,当可以让 forEach 默认有序,然后在流实例上调用 unordered 方法,就没有必要再定义新的方法了。
不幸的是,我在Java 8方面的实际经验还相当有限,因此我真的希望有人能解释一下这个架构决策的原因,最好能够用一些简单的示例/用例向我展示如果没有这样做可能会出现什么问题。
仅仅想要明确一下,我不是在问这个:forEach vs forEachOrdered in Java 8 Stream。我完全了解这些方法的工作方式和它们之间的区别。我询问的是 Oracle 做出这个架构决策的实际原因。

这样做会牺牲并行性的好处。 - Naman
如何在“默认排序”的情况下实现“跳过”和“限制”,而不会牺牲并行性的好处?有些人可能会说他们只是遵循了POLA的方法,但坦率地说,为“forEach”设置一个异常(这总是不好的)是更令人惊讶的主观例外情况。 - Gaponenko Andrei
1
你为什么期望forEach按照特定的顺序执行操作呢?这并不是“for each…”这个术语所暗示的。 - Holger
1
我更改了标题,删除了“默认情况下”和“并行流”,因为forEach通常是无序的,它不是可以在某种程度上更改的默认值,即使顺序流今天可能无法利用此属性,但它被定义为无序操作。 - Holger
"实际原因" - 如果您有较少的限制要遵守,那么更容易实现...和"Oracle做出的决定",那么可能(希望)Oracle知道得更好。 - user85421
3个回答

3

定义一个方法forEach来保留顺序,定义一个unordered方法来打破顺序,这会使事情变得复杂; 因为unordered仅在流API内部设置一个标志,必须根据某些条件执行或强制执行标志检查。

因此,假设您要执行以下操作:

someStream()
      .unordered()
      .forEach(System.out::println)

在这种情况下,您的建议是不按任何顺序打印元素,因此在此处强制执行“unordered”。但如果我们这样做会怎样:
someSet().stream()
         .unordered()
         .forEach(System.out::println)

在这种情况下,您是否希望强制执行unordered?毕竟,流的源是一个Set,它没有顺序,所以在这种情况下,强制执行unordered是无用的;但这意味着在流的源内部进行额外的测试。这可能会变得非常棘手和复杂(顺便说一句,它已经很复杂了)。
为了使其更简单明了,定义了两种方法,它们清楚地规定了它们将要做的事情;这与例如findFirstfindAny或者Optional::isPresentOptional::isEmpty(在java-11中添加)相当。

3
当你在并行处理Stream的元素时,不应该期望任何顺序保证。
整个想法是多个线程处理该流的不同元素。它们分别进行,因此处理的顺序是不可预测的。它是不确定的,也就是说是随机的。
我可以想象实现该接口的人故意给你随机顺序,以使得当使用并行流时不应该期望任何明显的顺序。

2
对于保留顺序的终端操作(没有会破坏顺序的中间操作),你总是会得到一个有序的输出。你所谈论的是“中间”操作的过程,而 forEach 不是其中之一。我已经多次阅读了你的答案,但我看不出它回答了提问者的问题。 - Eugene
2
当您在并行处理 Stream 元素时,您不应期望任何有关顺序的保证。但是,List.of(1,2,3,4).stream().parallel().collect(Collectors.toList()) 将保留相同的输出顺序,即使您正在并行处理元素。另外,“多个线程处理流的不同元素” 这一概念适用于中间阶段,而非终端阶段,而 OP 所问的是 forEach(终端操作)。我不知道……也许只是我的问题,但这最多是误导性的。 - Eugene

3

findFirstlimitskip这样的方法需要输入顺序,因此无论我们使用并行流还是串行流,它们的行为都不会改变。然而,forEach作为一个方法不需要任何顺序,因此它的行为是不同的。

对于并行流管道,forEach操作不能保证尊重流的遇到顺序,因为这样做将牺牲并行性的好处。

我还建议不要在并行流中使用findFirstlimitskip,因为这会降低性能,需要为排序并行流的开销。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接