What is the reason “forEach” in Java Streams API is unordered?
据我所知,在并行流中,只要流被排序(默认情况下),无论是否并行,findFirst,skip,limit等方法都会保持其行为。 。所以我想知道为什么forEach方法不同。我给了它一些想法,但是我无法理解定义forEachOrdered方法的必要性,当默认情况下使forEach排序然后在流实例上调用unordered时会更容易和更不令人惊讶,那就是它,无需定义新方法。
不幸的是,我对Java 8的实践经验在这一点上非常有限,所以如果有人可以向我解释这个架构决策的原因,也许有一些简单的例子/用例来向我展示可能出现的问题,我真的很感激。
为了说清楚,我不是在问这个:在Java 8 Stream中forEach vs forEachOrdered。我完全清楚这些方法是如何工作的以及它们之间的区别。我要问的是Oracle做出架构决策的实际原因。
-
这样做会牺牲并行性的好处....
-
它如何"跳过"和"限制"具有"默认排序"行为并不会牺牲并行性的好处? 有些人可能会说他们只是按照这些方法跟随POLA,但坦率地说,只有"forEach"的例外(总是很糟糕)主观上更令人惊讶。
-
您期望forEach以任何特定顺序应用操作的原因是什么?"每个......"这个词并不暗示
-
我更改了标题,删除了"默认情况下"和"并行流",因为forEach一般是无序的,它不是默认值,可能会以某种方式改变,即使顺序流今天不利用此属性,它也被定义为无序操作。
-
"实际原因" - 如果你有更少的限制要遵守......更容易实现......"Oracle做出的决定"那么可能(希望)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::isPresent和Optional::isEmpty(在java-11中添加)相提并论。
诸如findFirst,limit和skip之类的方法需要输入顺序,因此无论我们使用并行还是串行流,它们的行为都不会改变。但是,forEach作为一种方法不需要任何顺序,因此它的行为是不同的。
对于并行流管道,forEach操作不保证遵守流的遭遇顺序,因为这样做会牺牲并行性的好处。
我还建议不要将findFirst,limit和skip与并行流一起使用,因为它会降低性能,因为订购并行流需要开销。
当您并行处理Stream的元素时,您根本不应该期望在订单上有任何保证。
整个想法是多个线程处理该流的不同元素。它们是单独进步的,因此处理顺序是不可预测的。它是不确定的,也就是随机的。
我可以想象实现该接口的人有意为您提供随机顺序,以便明确表示在使用并行流时您不会期望任何不同的顺序。
-
对于保留订单的终端操作(并且没有会破坏它的中间操作),您将始终获得有序输出。 你所说的是中间操作的过程而forEach不是一个。 我也读了几次你的答案,我看不出它回答了OP的问题
-
例如,当您并行处理Stream的元素时,您根本不应该期望在订单上有任何保证,但是List.of(1,2,3,4).stream().parallel().collect(Collectors.toList())将在输出上保留相同的顺序,即使您正在处理并行流中的元素。 或者整个想法是多个线程在该流的不同元素上工作 - 这是关于中间阶段,而不是终端阶段,并且OP询问forEach(终端1)。 我不知道......可能是我在这里,但这充其量是误导