Java stream 中 peek() 的合理用法

本文介绍了Java Stream API中peek()和forEach()的区别与使用场景。peek()作为非最终操作,常用于调试,打印流经的元素,而forEach()是一个最终操作,确保对每个元素执行指定操作。在使用时,根据是否改变元素状态或仅观察选择合适的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

这周遇到了一件很有意思的事情。在看项目代码时,发现了这么一段:

return objects.stream()
	       .peek(object -> addInfo(object, someParams))
	       .collect(Collectors.toList());

因为之前没接触过 peek(),这段代码看得我云里雾里。

后来在 这个链接 里读到这样一句话:

On top of that, peek() can be useful in another scenario: when we want to alter the inner state of an element.

翻译过来就是:

除此之外,peek() 还有另一种用途:改变元素的内部状态

这样一来,就不难理解上面代码的意图了:对每个 object 执行 addInfo(object, someParams),然后把结果收集到一个 List 里。

但后来又看了一些讨论,发现这个用法在这里虽然不算错,但并不是 peek() 最合理的用法。

最终操作(terminal operation)

为什么呢?因为 peek() 并不是一个最终操作(terminal operation)。出于性能考虑,stream 被设计为“元素只有在最终操作需要时才会被处理”。如果没有最终操作的“拉动”,那么 stream 中就没有操作会真正执行。

在上面的例子中,.collect(Collectors.toList()) 就是一个最终操作,而且这个操作会“拉动”所有元素。这样一来,每个元素都一定会被应用 peek() 方法,所以放在这里没有问题。

但在使用 peek() 时仍然需要注意,因为它只保证作用于流经管道的元素,但并不保证全部元素都会流经管道。

peek() vs forEach()

forEach() 则是一个最终操作。除此之外,peek()forEach() 再无其他不同。

顾名思义,forEach() 就是保证对 stream 里的每个元素都应用某个方法。如果这是你的意图,那么最好用 forEach() 而不是 peek(),这样代码会便于理解。如果上面的代码用的是 forEach(),那我一开始也不至于看不懂了。

peek() 的典型用法:协助调试

正因为 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());

小结

  • 如果想对流经的每个元素应用一个函数,从而改变某些状态,那么请用 forEach()
  • 如果想打印流经的每个元素的状态(日志或 debug),这时应该用 peek()

参考链接

In Java streams is peek really only for debugging?
Non-terminal forEach() in a stream?

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值