1. 简介

Java Stream API 为我们提供了一种强大的方式来处理数据集合。它支持链式调用、延迟执行等特性,极大提升了代码的可读性和性能。

在本篇文章中,我们将聚焦于一个经常被误解的方法:peek()

2. 快速上手示例

先来看一个简单的例子。假设我们有一个字符串流,想要将其中每个名字打印到控制台。

由于 peek() 接收一个 Consumer<T> 类型的参数,看起来非常适合用来做这件事,于是我们尝试如下写法:

Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.peek(System.out::println);

然而,这段代码并不会有任何输出!

为什么会这样?我们需要回顾一下 Stream 的生命周期机制才能理解。

3. 中间操作 vs 终止操作

Stream 的处理流程可以分为三个部分:

  • 数据源(source)
  • 零个或多个中间操作(intermediate operations)
  • 零个或一个终止操作(terminal operation)

其中,中间操作是惰性求值的,也就是说,在没有触发终止操作之前,任何中间操作都不会真正执行。

⚠️ 这就是为什么上面那段代码没有输出的原因 —— 因为没有调用终止操作!

4. peek() 的正确使用姿势

回到最初的例子,既然 peek() 是中间操作,那我们必须搭配一个终止操作来“激活”整个流水线。比如我们可以加上 .forEach(...)

Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.peek(System.out::println)
          .forEach(System.out::println); // 加上终止操作

不过如果你只是想遍历并打印元素,直接用 forEach() 更加简单粗暴:

Stream<String> nameStream = Stream.of("Alice", "Bob", "Chuck");
nameStream.forEach(System.out::println);

peek() 的主要用途:调试和状态修改

根据 Javadoc 描述:

“This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline.”

也就是说,peek() 最常见的用途是调试,用于观察流经管道某个阶段的数据。

举个官方文档中的例子:

Stream.of("one", "two", "three", "four")
  .filter(e -> e.length() > 3)
  .peek(e -> System.out.println("Filtered value: " + e))
  .map(String::toUpperCase)
  .peek(e -> System.out.println("Mapped value: " + e))
  .collect(Collectors.toList());

在这个例子中,peek() 可以帮助我们看到每一步操作的结果,非常适合排查问题。

peek() 还能做什么?

除了调试,peek() 还有一个常见场景是修改对象内部状态,而不需要替换整个对象。

例如,我们要把所有用户的姓名转成小写后再打印:

Stream<User> userStream = Stream.of(new User("Alice"), new User("Bob"), new User("Chuck"));
userStream.peek(u -> u.setName(u.getName().toLowerCase()))
          .forEach(System.out::println);

如果用 map() 实现,虽然也能做到,但语义上就不如 peek() 清晰 —— 毕竟我们并不想改变元素本身的身份,只是想对其做一些副作用操作。

5. 小结

在这篇短文中,我们回顾了 Stream 的生命周期机制,并重点介绍了 peek() 方法的使用方式。

总结几个关键点:

peek() 是中间操作,必须配合终止操作才会生效
✅ 主要用于调试,观察中间结果
✅ 在需要对元素做副作用操作(如修改状态)时也非常实用
❌ 不要把它当成 forEach() 来使用,除非你知道自己在做什么

一如既往,文中示例代码可以在 GitHub 仓库 中找到。


原始标题:Java 8 Streams peek() API