【问题标题】:Method peek() shows different results in stream (peek() and findAny() together)方法 peek() 在流中显示不同的结果(peek() 和 findAny() 在一起)
【发布时间】:2022-11-30 20:56:23
【问题描述】:

我尝试学习不同流 API 方法的工作逻辑,我对我的简短示例有一个疑问:

public static void main(String[] args) {
    List<Integer> list = List.of(1,2,3,4,5,6,7,8,9);
    list.stream()
            .peek(i -> System.out.println(i))
            .filter(i -> i>4)
            .peek(i -> System.out.println(i))
            .findAny();
}

我得到以下输出:1 2 3 4 5 5。但是如果我删除第一个 peek(),我只有 5 个。为什么我没有再次获得值列表?我无法从官方文档中获得通用逻辑,该文档具有“返回由该流的元素组成的流,被截断为长度不超过 maxSize。”

你能解释一下在这个例子中使用两种方法 peek() 的区别吗?

【问题讨论】:

  • Why am I not getting the list of values again? - 因为第二个peek()filter() 之后。只有通过 filter() 的元素才会被打印。因为流是懒惰的,即每个操作仅在需要时发生。这是 Streams(和 Java 8 函数)的关键特性。您可以在 API Documentation 中找到此信息
  • 好的,知道了谢谢!但是,如果它在过滤器之前,为什么我在第一个 peek() 中只得到 [1,5]?
  • 第一个peek()消耗管道处理的所有元素,第二个只能消耗5,因为filter()不允许其他元素通过。不要混淆 Streams 和循环,它们的行为不同的.我在答案中给出了更详尽的解释。

标签: java list java-stream


【解决方案1】:

peek,顾名思义,只是让您“在流运行时查看流”。它是一种调试辅助工具。

返回一个流,该流由该流的元素组成,被截断为长度不超过 maxSize。

这是您未调用的 limit 方法的 javadoc。也许您感到困惑是因为您读错了 javadoc。

让我们一步步过一遍:

list.stream()

这建立了一个流。流是首先需要“处理”的东西,就其本身而言,这只是一个准备好“运行”的对象,但您需要先让它运行。我们将在接下来的步骤中介绍它。

这就像创建一个水可以流过的管道,但还没有泵来“拉”水。

.peek(i -> System.out.println(i))

这只是说:“如果“它运行,打印流过的东西。这就像在管道中安装一个窗口(还没有水流过它,并且打开一个窗口并不能改变这个事实)。

.filter(i -> i > 4)

这会在管道中插入一个过滤器,该过滤器“捕获”并丢弃谓词所做的任何事情不是hold:流过此管道的任何具有 i &gt; 4 不会解析为 true 属性的水分子(即 4 及以下的所有内容)都会被此过滤器捕获并丢弃。仍然没有水,你只是在建造一条管道,如果水流过它,它就会起作用。

.peek(...)

我们安装另一个窗口,这次是在过滤器之后。

.findAny()

啊,一个“终端”——那是一个泵,它实际上会开始从溪流中抽水!该泵将通过管道泵送水,直到出现任何水为止,然后返回任何出现的水并很快停止,因为没有进一步的意义来泵送更多的水。这会导致级联,现在一切都会发生。

这然后解释了你所看到的:

流通过管道泵送 1/2/3/4/5/6/7/8/9。 “1”命中第一个“窗口”并被打印,然后它命中过滤器并被丢弃。第二个窗口看不到它,findAny 泵也看不到它,所以它一直通过管道抽水。 2/3/4 相同 - 第一个窗口看到它,过滤器丢弃它,findAny() 不断拉水。

然后 5 通过第一个窗口,设法通过过滤器(它不再被过滤掉),到达第二个窗口,因此第二次打印。它终于到达了泵。因此这个泵会自行关闭,它知道它需要的一切,它只需要返回任何值,而“5”是一个值,因此它被返回。

6/7/8/9 已准备好通过管道被拉出,但“泵”已关闭,因此您永远看不到它们。他们甚至从来没有到达第一个窗口 - 他们为什么要这样做?那将是愚蠢和低效的,泵已经有了答案(5)。

注意:终端是 Stream 对象具有的任何方法不要本身返回一个 Stream 对象。所以,findAnycollectcountmax 等等。

【讨论】:

    【解决方案2】:

    为什么我没有再次获得值列表?

    流是懒惰的,即每个操作仅在需要时发生。

    这是来自 API Documentation 的引述:

    懒惰寻求.许多流操作,如过滤、映射或重复删除,可以延迟实现,从而提供优化机会。例如,“查找第一个包含三个连续元音的字符串”不需要检查所有输入字符串。 Stream 操作分为中间(Stream-producing)操作和终端(value-or side-effect-producing)操作。中间操作总是懒惰的。

    filter()peek()操作都是为了处理元素而设计的懒洋洋, 一次一个。

    IE。来自流源(在本例中为列表)的每个元素首先将被第一个 peek() 消耗,然后 filter() 被执行。第二个peek() 的动作将只调用能够通过过滤器的元素。

    笔记:该流不是循环,在循环中将为每个元素触发指定的操作(除非有一些条件逻辑)。流和循环的行为不同。

    这是来自 peek() 的 javadoc 的引述:

    参数:

    行动- 对元素执行的非干扰操作 是从流中消耗

    只有元素 5 被第二个 peek() 消耗。

    【讨论】:

      猜你喜欢
      • 2021-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多