Java流——惰性求值、合并和短路

6
我试图形成一个简明而连贯的理解,关于Java Streams API内lazy evaluation(惰性计算)的应用。以下是我目前的理解:
- 元素只有在需要时才被消耗,即streams具有惰性,中间操作也具有惰性,例如过滤器只在必要时才进行过滤。 - 中间操作可以合并在一起(如果它们是无状态的)。 - 短路操作不需要处理整个stream。
我的目标是将所有这些想法融合在一起,并确保我没有误传任何信息。我感到棘手的原因是每当我阅读有关Java Streams的任何文献时,它都会说它们是lazy或利用了lazy evaluation,然后开始交换优化技巧,例如融合和短路操作。
那么,以下说法是否正确?
- 融合是如何实现流API中的lazy evaluation的 - 即元素被消耗,并且尽可能合并操作。 如果没有融合,那么我们肯定会回到急切求值作为替代方案,因为另一种方法将是在执行下一个操作之前为每个中间操作处理所有元素。 - 短路操作在没有融合或lazy evaluation的情况下仍然可能发生,但在streams的上下文中,这两个原则的实施非常有帮助。
我希望能够得到更多深入的见解和澄清。
1个回答

52

关于融合。假设这里有一个map操作:

.map(x -> x.squash())

地图

它是无状态的,只是根据指定的算法(在我们的例子中压缩它们)转换任何输入。现在是过滤操作:

.filter(x -> x.getColor() != YELLOW)

筛选器

这个操作也是无状态的,它只会删除一些元素(在我们的例子中是黄色的元素)。现在让我们进行一个终端操作:

.forEach(System.out::println)

显示

它只是在终端上显示输入元素。合并意味着所有中间的无状态操作都与终端消费者合并为单个操作:

.map(x -> x.squash())
.filter(x -> x.getColor() != YELLOW)
.forEach(System.out::println)

Fuse

整个流程被融合成一个单独的Consumer,直接连接到源头。当每个元素都被处理后,源头分裂器只会执行组合消费者,流水线不会拦截任何东西也不进行任何额外的记录。这就是融合。融合不依赖于短路。可以实现无融合的流(执行一个操作,获取结果,执行下一个操作,获取每个操作控制权返回到流引擎)。也有可能有融合但没有短路。


@Tagir Valeev 感谢您提供非常直观的答案 - 非常有帮助。然而,我觉得您没有回答我的一些最初的问题,即:“融合是如何在流API中实现惰性求值的 - 我认为如果不存在融合,那么我们肯定会回到渴望求值”,并且“短路是否可以在没有融合的情况下实现?”如果可以的话,它只会是一个急切求值版本,仅在终端操作上停止而不是在处理中间操作上停止。 - Tranquility
2
@Tranquility,我刚刚解释了融合是将多个操作合并在一起的一种方式。您可以逐步执行它们而不进行融合,并且仍然具有惰性评估。 - Tagir Valeev
1
@Tagir Valeev 当您说“逐步”时,是否仍然意味着一次处理单个元素,即根据需要消耗元素,但只需分别运行中间和终端操作而不是作为一个融合(和优化)操作全部运行?我认为融合是允许独立消耗元素而无需处理其他元素(急切地)的事情,但似乎您提出了不同的建议。 - Tranquility

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接