Java stream 中 peek() 的合理用法

背景

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

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?

  • 42
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
stream.peek方法的作用是在流的每个元素上执行指定的操作,在不修改流元素的情况下返回一个新的流。它接受一个Consumer类型的参数,该参数指定了要执行的操作。在间操作使用peek方法可以方便地对流进行调试,了解每个操作之前和操作之后的间值。 在引用的代码,虽然调用了stream.peek(System.out::println),但没有任何输出。这是因为peek方法是一个间操作,它只会在终止操作被调用时才会执行。在代码,并没有调用任何终止操作,所以没有输出。如果想要输出流的值,可以在peek方法后添加一个终止操作,例如forEach方法来打印出流的元素。 下面是修改后的代码示例: ```java public class PeekTestTwo { public static void main(String[] args) { Stream<Integer> stream = Arrays.asList(4, 7, 9, 11, 12).stream(); stream.peek(System.out::println).forEach(System.out::println); } } ``` 现在,当运行这段代码时,会先输出每个元素的值,然后再打印出流的元素值。这是因为在peek方法,我们指定了打印每个元素的操作,而在forEach方法,我们又指定了打印流元素的操作。这样就能够看到每个操作之前和操作之后的间值。 请注意,peek方法不会改变流的元素,它只是提供了一种便捷的方式来查看流每个元素的值。要改变流的元素,需要使用其他的间操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值