在Java 8中,顺序和有序流保证按顺序执行操作吗?

时间:2016-05-17 13:44:38

标签: java java-stream

是否可以保证在遇到订单顺序订购流的操作已处理?< / p>

我的意思是,如果我有这样的代码:

IntStream.range(0, 5)
        .map(i -> {
            myFunction(i);
            return i * 2;
         })
         .boxed()
         .collect(toList());

是否有保证,它会以生成范围的遭遇顺序执行myFunction()调用?

我发现draft JavaDocs for the Stream类明确说明了这一点:

  

对于顺序流管道,如果管道源具有已定义的遭遇顺序,则所有操作都以管道源的遭遇顺序执行。

但在official JavaDocs这条线被删除了。它现在仅讨论所选方法的遭遇顺序。 副作用段落中的Package java.util.stream doc声明:

  

即使管道被约束以产生与流源的遭遇顺序一致的结果(例如,IntStream.range(0,5).parallel().map(x -> x*2).toArray()必须产生[0, 2, 4, 6, 8]),也不保证订单其中mapper函数应用于单个元素,或者在什么线程中为给定元素执行任何行为参数。

但它没有说顺序流,示例是并行流(我的理解是顺序和并行流都是如此,但这是我不确定的部分)。

另一方面,它也在Ordering部分说明:

  

如果订购了流,则大多数操作都被约束为对其遭遇顺序中的元素进行操作;如果流的来源是包含List的{​​{1}},则执行[1, 2, 3]的结果必须为map(x -> x*2)。但是,如果源没有定义的遭遇顺序,那么值[2, 4, 6]的任何排列都将是有效的结果。

但是这次它以“对元素进行操作”开始,但是示例是关于生成的流,所以我不确定它们是否会考虑副作用,副作用实际上是这个问题的关键所在

1 个答案:

答案 0 :(得分:1)

我认为我们可以从这个明确的句子被删除的事实中学到很多东西。这个问题似乎与问题“Does Stream.forEach respect the encounter order of sequential streams?”密切相关。 answer from Brian Goetz基本上表示,尽管在顺序流上调用forEach时,Stream的当前实现没有忽略该顺序的事实,forEach 已经 em>即使对于每个规范的顺序Streams,也可以自由地忽略遭遇顺序。

现在考虑Stream’s class documentation的以下部分:

  

要执行计算,流操作将组成一个流管道。流管道由源(可能是数组,集合,生成器函数,I / O通道等),零个或多个中间操作(将流转换为另一个流)组成,例如filter(Predicate))和终端操作(产生结果或副作用,例如count()forEach(Consumer))。溪流很懒;源数据的计算仅在启动终端操作时执行,源元素仅在需要时使用。

由于是终端操作确定是否需要元素以及是否在遭遇顺序中需要它们,因此终端动作忽略遭遇顺序的自由也意味着以任意顺序消耗,因此处理元素。

请注意,forEach不仅可以做到这一点。 Collector能够报告UNORDERED特征,例如Collectors.toSet()不依赖于遭遇顺序。很明显,像count()这样的操作也不依赖于Java 9中的顺序 - 它甚至可以在没有任何元素处理的情况下返回。想想IntStream#sum()的另一个例子。

过去,实现过于急于在流中传播无序特征,请参阅“Is this a bug in Files.lines(), or am I misunderstanding something about parallel streams?”终端操作影响skip步骤结果的原因,这就是< em>当前实现不愿意这样的优化以避免类似的错误,但这并不排除这种优化的再次出现,然后更谨慎地实现......

所以目前很难想象一个实现如何通过在顺序Stream中利用无序评估的自由来获得性能优势,但是,正如forEach相关问题所述,那不是'这意味着任何保证。