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 【问题描述】:据我所知,在并行流中,findFirst
、skip
、limit
等方法只要流是有序的(即默认情况下)是否并行。所以我想知道为什么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】:
findFirst
、limit
和 skip
等方法需要输入顺序,因此无论我们使用并行流还是串行流,它们的行为都不会改变。但是,forEach
作为方法不需要任何顺序,因此它的行为是不同的。
对于并行流管道,forEach
操作不保证尊重流的遇到顺序,因为这样做会牺牲并行性的好处。
我还建议不要将findFirst
、limit
和skip
用于并行流,因为订购并行流所需的开销会降低性能。
【讨论】:
【参考方案3】:定义一个方法 forEach
可以保持秩序,unordered
会破坏它,这会使 IMO 的事情变得复杂;仅仅因为unordered
只是在流 api 内部设置一个标志,并且必须根据某些条件执行或强制执行标志检查。
假设你会这样做:
someStream()
.unordered()
.forEach(System.out::println)
在这种情况下,您的建议是不要以任何顺序打印元素,因此 强制 unordered
在这里。但如果我们这样做了:
someSet().stream()
.unordered()
.forEach(System.out::println)
在这种情况下,您是否希望强制执行 unordered
?毕竟流的来源是Set
,它没有顺序,所以在这种情况下,强制unordered
是没有用的;但这意味着在内部对流的源进行额外的测试。这可能会变得非常棘手和复杂(因为它已经是顺便说一句)。
为了简单起见,定义了两种方法,明确规定了它们将做什么;这与 findFirst
与 findAny
甚至 Optional::isPresent
和 Optional::isEmpty
(在 java-11 中添加)相当。
【讨论】:
以上是关于Java Streams API中的“forEach”无序的原因是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章