Java流操作融合和有状态中间操作

时间:2016-01-28 18:19:10

标签: java java-8 java-stream sorted stateful

我一直在努力理解和展示Java流如何在引擎盖下实现一种循环融合,以便可以将多个操作融合到一个传递中。

这是第一个例子:

Stream.of("The", "cat", "sat", "on", "the", "mat")
        .filter(w -> {
            System.out.println("Filtering: " + w);
            return w.length() == 3;
        })
        .map(w -> {
            System.out.println("Mapping: " + w);
            return w.toUpperCase();
        })
        .forEach(w -> System.out.println("Printing: " + w));

具有以下输出(每个元素的单个传递的融合非常清楚):

Filtering: The
Mapping: The
Printing: THE
Filtering: cat
Mapping: cat
Printing: CAT
Filtering: sat
Mapping: sat
Printing: SAT
Filtering: on
Filtering: the
Mapping: the
Printing: THE
Filtering: mat
Mapping: mat
Printing: MAT

第二个例子是相同的,但是我使用filter和map之间的sorted()操作:

Stream.of("The", "cat", "sat", "on", "the", "mat")
        .filter(w -> {
            System.out.println("Filtering: " + w);
            return w.length() == 3;
        })
        .sorted()
        .map(w -> {
            System.out.println("Mapping: " + w);
            return w.toUpperCase();
        })
        .forEach(w -> System.out.println("Printing: " + w));

这有以下输出:

Filtering: The
Filtering: cat
Filtering: sat
Filtering: on
Filtering: the
Filtering: mat
Mapping: The
Printing: THE
Mapping: cat
Printing: CAT
Mapping: mat
Printing: MAT
Mapping: sat
Printing: SAT
Mapping: the
Printing: THE

所以我的问题在这里,通过调用distinct,我认为因为它是一个“有状态”的中间操作,所以它不允许在单次传递(所有操作)中单独处理单个元素。此外,因为sorted()有状态操作需要处理整个输入流以产生结果,所以这里不能部署融合技术,这就是为什么所有的过滤首先发生,然后它将映射和打印操作融合在一起的原因,排序后?如果我的任何假设不正确,请纠正我,并随时详细说明我已经说过的内容。

此外,它如何决定是否可以将元素融合在一起进行单次传递,例如,当存在distinct()操作时,是否只有一个标志会关闭以阻止其发生当distinct()不存在时会这样做吗?

最后一个问题是,虽然将操作融合到单个通道中的好处有时是显而易见的,例如,当与短路结合时。融合操作的主要好处是什么,例如filter-map-forEach,甚至是filter-map-sum?

2 个答案:

答案 0 :(得分:14)

无状态操作(map,filter,flatMap,peek等)完全融合;我们建立了一系列级联Consumer对象并将数据倒入。每个元素可以彼此独立地操作,因此从来没有任何东西"卡住"在链中。 (这就是路易斯的意思是如何实现融合 - 我们将这些阶段组合成一个大功能,然后将数据提供给它。)

有状态操作(不同,排序,限制等)更复杂,并且其行为的变化更大。每个有状态操作都可以选择它想要实现的方式,因此它可以选择可能性最小的方法。例如,distinct(在某些情况下),允许元素在审核时出现,而sorted则是完全屏障。 (不同之处在于可能存在多少懒惰,以及它们如何处理下游限制操作等无限来源的事情。)

有状态的操作通常会破坏融合的一些好处,但不是所有的好处(上游和下游的操作仍然可以融合。)

除了您观察到的短路值之外,融合的其他重大胜利包括:(a)您不必在阶段之间填充中间结果容器,以及(b)您正在处理的数据总是"热"在缓存中。

答案 1 :(得分:4)

是的,这是关于正确的。所有这些都可以通过查看源代码来检查。

但是,Fusion并没有像我认为的那样实现。没有看整个管道并决定如何融合它;没有旗帜或任何东西;它只是将操作表示为StatefulOp对象,它可以将整个流运行到该点并获得所有输出,或者只是StatelessOp来装饰{{1}说明元素的去向。您可以查看源代码,例如:例如sortedmap