Java Streams API中的“forEach”是无序的原因是什么?

What is the reason “forEach” in Java Streams API is unordered?

本问题已经有最佳答案,请猛点这里访问。

据我所知,在并行流中,只要流被排序(默认情况下),无论是否并行,findFirstskiplimit等方法都会保持其行为。 。所以我想知道为什么forEach方法不同。我给了它一些想法,但是我无法理解定义forEachOrdered方法的必要性,当默认情况下使forEach排序然后在流实例上调用unordered时会更容易和更不令人惊讶,那就是它,无需定义新方法。

不幸的是,我对Java 8的实践经验在这一点上非常有限,所以如果有人可以向我解释这个架构决策的原因,也许有一些简单的例子/用例来向我展示可能出现的问题,我真的很感激。

为了说清楚,我不是在问这个:在Java 8 Stream中forEach vs forEachOrdered。我完全清楚这些方法是如何工作的以及它们之间的区别。我要问的是Oracle做出架构决策的实际原因。


定义一个保留顺序的方法forEach和会破坏它的unordered会使IMO复杂化;只是因为unordered只是在流api内部设置一个标志,并且必须根据某些条件执行或强制执行标志检查。

所以,假设你会这样做:

1
2
3
someStream()
      .unordered()
      .forEach(System.out::println)

在这种情况下,您的建议是不以任何顺序打印元素,从而在此处强制执行unordered。但是如果我们这样做了:

1
2
3
someSet().stream()
         .unordered()
         .forEach(System.out::println)

在这种情况下,您希望unordered被强制执行吗?毕竟,流的源是Set,它没有顺序,所以在这种情况下,强制执行unordered是没用的;但这意味着在内部对流源进行额外测试。这可能变得相当棘手和复杂(因为它已经是顺便说一句)。

为了简单起见,定义了两种方法,明确规定了它们的作用;这与例如findFirst vs findAny或甚至Optional::isPresentOptional::isEmpty(在java-11中添加)相提并论。


诸如findFirstlimitskip之类的方法需要输入顺序,因此无论我们使用并行还是串行流,它们的行为都不会改变。但是,forEach作为一种方法不需要任何顺序,因此它的行为是不同的。

对于并行流管道,forEach操作不保证遵守流的遭遇顺序,因为这样做会牺牲并行性的好处。

我还建议不要将findFirstlimitskip与并行流一起使用,因为它会降低性能,因为订购并行流需要开销。


当您并行处理Stream的元素时,您根本不应该期望在订单上有任何保证。

整个想法是多个线程处理该流的不同元素。它们是单独进步的,因此处理顺序是不可预测的。它是不确定的,也就是随机的。

我可以想象实现该接口的人有意为您提供随机顺序,以便明确表示在使用并行流时您不会期望任何不同的顺序。