Java Streams API中的“forEach”无序的原因是啥? [复制]

Posted

技术标签:

【中文标题】Java Streams API中的“forEach”无序的原因是啥? [复制]【英文标题】:What is the reason "forEach" in Java Streams API is unordered? [duplicate]Java Streams API中的“forEach”无序的原因是什么? [复制] 【发布时间】:2019-02-18 20:52:48 【问题描述】:

据我所知,在并行流中findFirstskiplimit 等方法只要流是有序的(即默认情况下)是否并行。所以我想知道为什么forEach 方法不同。我想了想,但我只是无法理解定义forEachOrdered 方法的必要性,因为默认情况下使forEach 排序可能更容易且不那么令人惊讶,然后在流实例上调用unordered,这就是它,无需定义新方法。

不幸的是,目前我对 Java 8 的实践经验非常有限,所以如果有人能解释我做出这个架构决定的原因,我将不胜感激,也许可以通过一些简单的示例/用例向我展示否则会出现什么问题.

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

【问题讨论】:

因为这样做会牺牲并行性的好处。... 它如何“跳过”和“限制”具有“默认排序”的行为并没有牺牲并行性的好处?有些人可能会说他们只是用这些方法遵循 POLA,但坦率地说,仅仅为“forEach”有一个例外(这总是不好的)在主观上更令人惊讶。 您期望forEach 以任何特定顺序应用操作的原因是什么? “为每个……”一词并不暗示它 我更改了标题,删除了“默认情况下”和“并行流中”,因为forEach通常是无序的,它不是默认值,可以以某种方式更改并且即使现在顺序流可能不会利用此属性,它也被定义为无序操作。 “实际原因”- 如果您遵守的约束较少...和“Oracle 做出的决定”,那么可能(希望)Oracle 知道得更好 【参考方案1】:

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

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

可以想象实现该接口的人故意给你随机的顺序,让你真正清楚不期望任何使用并行流时的不同顺序。

【讨论】:

对于保留顺序的终端操作(并且没有会破坏它的中间操作),您总是会得到一个有序的输出。你说的是intermediate操作的过程,forEach不是一个。我也读了几次你的答案,但我看不到它回答了 OP 的问题 例如 当您并行处理 Stream 的元素时,您根本不应该期望任何订单保证,但List.of(1,2,3,4).stream().parallel().collect(Collectors.toList()) 将保留相同的输出顺序,即使如果您正在处理并行流中的元素。或者 整个想法是多个线程在该流的不同元素上工作 - 这是关于中间阶段,而不是终端阶段,并且 OP 正在询问 forEach(终端一)。我不知道...可能是我在这里,但这充其量是误导【参考方案2】:

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

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

我还建议不要将findFirstlimitskip 用于并行流,因为订购并行流所需的开销会降低性能。

【讨论】:

【参考方案3】:

定义一个方法 forEach 可以保持秩序,unordered 会破坏它,这会使 IMO 的事情变得复杂;仅仅因为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 中添加)相当。

【讨论】:

以上是关于Java Streams API中的“forEach”无序的原因是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解

Java 8 中的 Streams API 详解