当前位置: 首页 > 知识库问答 >
问题:

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

龙俭
2023-03-14

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

根据JavaDoc:

Peek()方法的存在主要是为了支持调试,您希望在元素流经管道中的某个点时看到它们。

我有一个10米的管道,在距离输入头3和7米的地方,我有两个标记[aka peek()]用于检查/调试我的元素。

现在从输入端开始,我给出<code>1,2,3,4,5</code>的输入。

在点x = 4米处,我有一个< code>filter()来过滤所有小于等于< code>3的元素。

现在根据Java文档,我应该能够看到我在管道中的输入在距离37米处发生了什么。

距离3处的标记1的输出(. peek())应该是1,2,3,4,5不应该是??距离7处的标记2的输出显然应该是4,5

但这实际上并没有发生,产出来自第一市场。peek())只有< code>1,2,3,第二个是< code>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() 不仅适用于在管道中的特定点进行html" target="_blank">调试,那么 def 就是模棱两可的。

对不起,我的烟斗的故事,我想这样我可以解释我想问的最好的。

共有2个答案

姚麒
2023-03-14

安德烈·艾哈迈托夫的回答是正确的,但我想补充一点,因为这里有两个问题。一个是流管道语义的一般问题 - 这确实是你的问题所在。第二个是关于peek()的含义和局限性。

对于主要问题——这与<code>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()是一个保持大小的操作。)总是在< code>peek()点提供元素的要求会破坏许多有用的优化。因此,如果实现可以证明它不会影响答案,它可以自由地省略整个管道的中间部分。(它可能会产生更少的副作用,但如果你这么在乎副作用,也许你不应该使用流。)

龚浩宕
2023-03-14

不会。流可以根据需要进行延迟计算,操作的顺序没有严格的定义,尤其是在< code>peek()操作时。这允许streams API支持非常大的流,而不会浪费大量的时间和内存,并且允许某些实现简化。特别地,在下一级被评估之前,流水线的单个级不需要被完全评估。

根据您的假设,假设以下代码会多么浪费:

IntStream.range(1, 1000000).skip(5).limit(10).forEach(System::println);

该流从100万个元素开始,以10个元素结束。如果我们完全评估每个阶段,我们的中间体将分别是100万、999995和10个元素。

作为第二个示例,以下流不能一次计算一个阶段(因为<code>IntStream.generate<code>返回无限流):

IntStream.generate(/* some supplier */).limit(10).collect(Collectors.toList());

您的管道确实通过第一个< code>peek传递每个元素,然后只通过第二个< code>peek传递一个子集。但是,管道以元素主要而不是阶段主要的顺序执行此评估:它评估管道的值为1,将其放在过滤器上,然后是2。一旦它为3评估了管道,它就传递过滤器,因此两个peek语句都执行,然后同样的情况发生在4和5上。

 类似资料:
  • 在Java的LinkedList中,peek和element有什么区别? 以下是OracleJava文档页面对它们的描述,但它们没有解释区别。 公共E peek() 检索但不删除此列表的头(第一个元素)。 指定者:界面中的peek Deque public E element() 检索但不删除列表的头部(第一个元素)。指定者:接口队列中的元素 区别仅仅是一个抛出异常,而另一个在列表为空的情况下返回

  • 我有一个管道可以解析AVRO文件中的记录。 我需要将传入的记录分成500个项目的块,以便调用一个同时接受多个输入的API。 有没有办法用PythonSDK做到这一点?

  • 问题内容: 我正在阅读有关Java流的信息,并在不断学习中发现新事物。我发现的新peek()功能之一就是功能。我偷看的几乎所有内容都说应将其用于调试Streams。 如果我有一个Stream,其中每个帐户都有一个用户名,密码字段以及一个login()和loggingIn()方法,该怎么办。 我也有 和 为什么会这么糟糕? 现在,据我所知,这确实可以实现预期的目的。它; 取得帐户清单 尝试登录每个帐

  • 我正在阅读有关Java流的内容,并在此过程中发现了一些新的东西。我发现的一个新东西是函数。我在peek上看到的几乎所有东西都说它应该用来调试流。 如果我有一个流,其中每个帐户都有用户名、密码字段和login()和loggedIn()方法。 我也有 而且 为什么会这么糟? null 做这样的事情有什么坏处?有什么理由不让我继续吗?最后,如果不是这个解决方案,那又是什么呢? 它的原始版本使用了.fil

  • 如果我有一个<代码>流 最明显的解决方案是使用<code>limit(originalLength-elementsToRemoveAtEnd),但这需要事先知道初始长度,这并不总是如此。 有没有一种方法可以删除长度未知的流的最后几个元素,而无需将其收集到中,计算元素并再次流式传输?

  • 问题内容: 我的页面上有一张表格,其中应该包含某个元素。我可以通过表名(具有唯一的名称)来标识表,并且还可以轻松地标识元素。我想断言元素存在于表的row ,column 上。使用Selenium命令最干净的方法是什么? 备注: 我不想使用除表名以外的其他内容来查找它(我不希望代码中的所有内容)。 我在PHPUnit中使用Selenium。因此,我可以将PHP逻辑用于该任务,尽管我不希望任何复杂的逻