为了账号安全,请及时绑定邮箱和手机立即绑定

Peek() 真正看到元素流过管道中的某个点

Peek() 真正看到元素流过管道中的某个点

www说 2022-05-25 16:46:30
我的问题以最简单的表达方式:根据JavaDoc:Peek()方法的存在主要是为了支持调试,您希望在元素流过管道中的某个点时查看它们。我有一个10 米长的管道,在距离 输入头3米和7 米peek()的地方,我有两个标记 [aka ] 用于检查/调试我的元素。现在从输入端我输入1,2,3,4,5.在点 x = 4 米处,我有一个filter()过滤所有小于和等于的元素3。现在根据Java doc,我应该能够看到我在管道中的输入在距离3和7米处发生了什么。距离 3 ( )处的标记1处的输出不应该是?并且在距离 7处的标记2 处的输出 应该很明显。.peek()1,2,3,4,54,5但这实际上并没有发生,输出只是在 1st market(.peek())1,2,3和在 2nd 它来了4,5。我为测试我的理论而执行的代码:final List<Integer> IntList=    Stream.of(1, 2, 3, 4, 5)    .peek(it -> System.out.println("Before Filtering "+it)) // should print 1,2,3,4,5    .filter(it -> it >= 3)    .peek(it -> System.out.println("After Filtering: "+it)) //should print 4,5    .collect(Collectors.toList());实际输出:Before Filtering 1Before Filtering 2Before Filtering 3After Filtering: 3Before Filtering 4After Filtering: 4Before Filtering 5After Filtering: 5预期输出(开发人员在阅读JavaDoc后应该想到什么(...主要用于支持调试,您希望在其中看到元素流过管道中的某个点...)    Before Filtering 1    Before Filtering 2    Before Filtering 3    Before Filtering 4    Before Filtering 5    After Filtering: 4    After Filtering: 5如果.peek()不仅仅是在管道中的特定点进行调试,那么 def 是不明确的。对不起我的 Pipe 故事,我认为这样我可以最好地解释我想问的问题。
查看完整描述

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 上发生相同的情况。


查看完整回答
反对 回复 2022-05-25
?
RISEBY

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()点处提供元素的要求会破坏许多有用的优化。因此,如果可以证明它不会影响答案,则实现可以自由地省略整个管道中间。(它可能会产生更少的副作用,但如果你非常关心副作用,也许你不应该使用流。)


查看完整回答
反对 回复 2022-05-25
  • 2 回答
  • 0 关注
  • 202 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号