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

我的问题以最简单的表达方式:


根据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 1

Before Filtering 2

Before Filtering 3

After Filtering: 3

Before Filtering 4

After Filtering: 4

Before Filtering 5

After 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 故事,我认为这样我可以最好地解释我想问的问题。


www说
浏览 215回答 2
2回答

森林海

不可以。 Streams 可能会根据需要延迟评估,并且操作顺序没有严格定义,尤其是在您peek()ing 时。这允许流 API 支持非常大的流,而不会浪费大量时间和内存,并允许某些实现简化。特别是,管道的单个阶段不需要在下一个阶段之前被完全评估。根据您的假设,假设以下代码将是多么浪费:IntStream.range(1,&nbsp;1000000).skip(5).limit(10).forEach(System::println);流以 100 万个元素开始,以 10 个元素结束。如果我们对每个阶段进行全面评估,我们的中间元素将分别为 100 万、999995 和 10 个元素。作为第二个示例,以下流不能一次评估一个阶段(因为IntStream.generate返回无限流):IntStream.generate(/*&nbsp;some&nbsp;supplier&nbsp;*/).limit(10).collect(Collectors.toList());您的管道确实通过第一个元素传递每一个元素peek,然后通过第二个元素只传递一个子集peek。但是,管道以元素优先而不是阶段优先顺序执行此评估:它评估管道为 1,将其放在过滤器上,然后是 2。一旦它评估管道为 3,它通过过滤器因此两者都偷看语句执行,然后在 4 和 5 上发生相同的情况。

RISEBY

Andrey Akhmetov 的回答是正确的,但我想补充一下,因为这里有两个问题。一个是流管道语义的一般问题——这正是您的问题所在。第二个是关于 的含义和限制peek()。对于主要问题 - 与 无关peek(),除了你如何观察正在发生的事情的状态 - 你对流的直觉是完全不正确的。没有理由相信:collection.stream() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter(x&nbsp;->&nbsp;x.foo()&nbsp;>&nbsp;3) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(X::toBar) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.forEach(b&nbsp;->&nbsp;System.out.println("Bar:&nbsp;"&nbsp;+&nbsp;b);所有过滤都发生在所有打印之前的所有映射之前。该流可以自由地以它喜欢的任何顺序交错过滤、映射和打印。(总体上有一些排序保证。)这里的好处是,在某些具有无限流的情况下,这通常更高效、更可并行且更健壮。只要您遵守规则(即,不要依赖一个阶段在另一个阶段的副作用),您将无法区分,除非您的代码运行得更快。的语言摇摆不定的原因peek()是对于管道,例如:int&nbsp;size&nbsp;=&nbsp;collection.stream() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.map(...) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.peek(...) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.count()我们可以在不进行任何映射的情况下评估答案(因为已知 map() 是一种保持大小的操作。)始终在peek()点处提供元素的要求会破坏许多有用的优化。因此,如果可以证明它不会影响答案,则实现可以自由地省略整个管道中间。(它可能会产生更少的副作用,但如果你非常关心副作用,也许你不应该使用流。)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java