2 回答
TA贡献2011条经验 获得超2个赞
不可以。 Streams 可能会根据需要延迟评估,并且操作顺序没有严格定义,尤其是在您peek()ing 时。这允许流 API 支持非常大的流,而不会浪费大量时间和内存,并允许某些实现简化。特别是,管道的单个阶段不需要在下一个阶段之前被完全评估。
根据您的假设,假设以下代码将是多么浪费:
IntStream.range(1, 1000000).skip(5).limit(10).forEach(System::println);
流以 100 万个元素开始,以 10 个元素结束。如果我们对每个阶段进行全面评估,我们的中间元素将分别为 100 万、999995 和 10 个元素。
作为第二个示例,以下流不能一次评估一个阶段(因为IntStream.generate返回无限流):
IntStream.generate(/* some supplier */).limit(10).collect(Collectors.toList());
您的管道确实通过第一个元素传递每一个元素peek,然后通过第二个元素只传递一个子集peek。但是,管道以元素优先而不是阶段优先顺序执行此评估:它评估管道为 1,将其放在过滤器上,然后是 2。一旦它评估管道为 3,它通过过滤器因此两者都偷看语句执行,然后在 4 和 5 上发生相同的情况。
TA贡献1856条经验 获得超5个赞
Andrey Akhmetov 的回答是正确的,但我想补充一下,因为这里有两个问题。一个是流管道语义的一般问题——这正是您的问题所在。第二个是关于 的含义和限制peek()。
对于主要问题 - 与 无关peek(),除了你如何观察正在发生的事情的状态 - 你对流的直觉是完全不正确的。没有理由相信:
collection.stream()
.filter(x -> x.foo() > 3)
.map(X::toBar)
.forEach(b -> System.out.println("Bar: " + b);所有过滤都发生在所有打印之前的所有映射之前。该流可以自由地以它喜欢的任何顺序交错过滤、映射和打印。(总体上有一些排序保证。)这里的好处是,在某些具有无限流的情况下,这通常更高效、更可并行且更健壮。只要您遵守规则(即,不要依赖一个阶段在另一个阶段的副作用),您将无法区分,除非您的代码运行得更快。
的语言摇摆不定的原因peek()是对于管道,例如:
int size = collection.stream() .map(...) .peek(...) .count()
我们可以在不进行任何映射的情况下评估答案(因为已知 map() 是一种保持大小的操作。)始终在peek()点处提供元素的要求会破坏许多有用的优化。因此,如果可以证明它不会影响答案,则实现可以自由地省略整个管道中间。(它可能会产生更少的副作用,但如果你非常关心副作用,也许你不应该使用流。)
添加回答
举报
