[英]Does Stream.forEach respect the encounter order of sequential streams?
[英]In Java 8, does a sequential and ordered Stream guarantee performing operations in encounter order?
是否有一个顺序和操作有序流在遭遇顺序 处理任何保证?
我的意思是,如果我有这样的代码:
IntStream.range(0, 5)
.map(i -> {
myFunction(i);
return i * 2;
})
.boxed()
.collect(toList());
是否有保证,它将以生成范围的遭遇顺序执行myFunction()调用?
我找到了Stream类的JavaDocs草案 ,明确说明了这一点:
对于顺序流管道,如果管道源具有已定义的遭遇顺序,则所有操作都以管道源的遭遇顺序执行。
但在官方JavaDocs中,这一行被删除了。 它现在仅讨论所选方法的遭遇顺序。 Side-effects段落中的Package java.util.stream doc说明:
即使管道被约束产生的结果与流源的遭遇顺序一致(例如,
IntStream.range(0,5).parallel().map(x -> x*2).toArray()
必须产生[0, 2, 4, 6, 8]
),不保证映射器函数应用于单个元素的顺序,或者对给定元素执行任何行为参数的线程。
但它没有说明顺序流,而且这个例子是针对并行流的(我的理解是顺序流和并行流都是如此,但这是我不确定的部分)。
另一方面,它也在订购部分说明:
如果订购了流,则大多数操作都被约束为对其遭遇顺序中的元素进行操作; 如果流的源是包含
[1, 2, 3]
的List
,那么执行map(x -> x*2)
必须是[2, 4, 6]
。 但是,如果源没有定义的遭遇顺序,那么值[2, 4, 6]
任何排列都将是有效的结果。
但这一次它以“对元素进行操作”开始,但是示例是关于产生的流,所以我不确定它们是否会考虑副作用,副作用实际上就是这个问题。
我认为我们可以从这个明确的句子被删除的事实中学到很多东西。 这个问题似乎与“Stream.forEach是否遵守顺序流的遭遇顺序?”这一问题密切相关。 Brian Goetz的答案基本上说,尽管在顺序Stream上调用forEach
时,Stream的当前实现没有忽略顺序的事实, forEach
可以自由地忽略遇到顺序,即使是每个规范的顺序Streams也是如此。
现在考虑Stream
的类文档的以下部分:
为了执行计算,流操作被组合成流管道 。 流管道由源(可能是数组,集合,生成器函数,I / O通道等),零个或多个中间操作 (将流转换为另一个流,如
filter(Predicate)
)和终端操作(产生结果或副作用,例如count()
或forEach(Consumer)
)。 溪流很懒; 仅在启动终端操作时执行对源数据的计算,并且仅在需要时消耗源元素。
由于终止操作确定是否需要元素以及在遭遇顺序中是否需要元素,因此终端动作忽略遭遇顺序的自由也意味着以任意顺序消耗,因此处理元素。
请注意,不仅forEach
都能做到这一点。 Collector
能够报告UNORDERED
特征,例如Collectors.toSet()
不依赖于遭遇顺序。 很明显,像count()
这样的操作也不依赖于Java 9中的顺序 - 它甚至可以在没有任何元素处理的情况下返回。 想想IntStream#sum()
的另一个例子。
在过去,实现过于急于在流中传播无序特征,请参阅“这是Files.lines()中的错误,还是我误解了有关并行流的内容?”终端操作影响了结果skip
步骤,这就是为什么当前的实现不希望这样的优化以避免类似的错误的原因,但这并不排除这种优化的再现,然后更谨慎地实现...
因此,目前很难想象一个实现如何通过在顺序流中利用无序评估的自由来获得性能优势,但是,正如forEach
相关问题所述,这并不意味着任何保证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.