网易首页 > 网易号 > 正文 申请入驻

还在stream中使用peek?不要被这些陷阱绊住了

0
分享至

简介

自从JDK中引入了stream之后,仿佛一切都变得很简单,根据stream提供的各种方法,如map,peek,flatmap等等,让我们的编程变得更美好。

事实上,我也经常在项目中看到有些小伙伴会经常使用peek来进行一些业务逻辑处理。

那么既然JDK文档中说peek方法主要是在调试的情况下使用,那么peek一定存在着某些不为人知的缺点。一起来看看吧。

peek的定义和基本使用

先来看看peek的定义:

Stream peek(Consumer action);

peek方法接受一个Consumer参数,返回一个Stream结果。

而Consumer是一个FunctionalInterface,它需要实现的方法是下面这个:

void accept(T t);

accept对传入的参数T进行处理,但是并不返回任何结果。

我们先来看下peek的基本使用:

public static void peekOne(){
Stream.of(1, 2, 3)
.peek(e -> log.info(String.valueOf(e)))
.toList();

运行上面的代码,我们可以得到:

[main] INFO com.flydean.Main - 1
[main] INFO com.flydean.Main - 2
[main] INFO com.flydean.Main - 3

逻辑很简单,就是打印出Stream中的元素而已。

peek的流式处理

peek作为stream的一个方法,当然是流式处理的。接下来我们用一个具体的例子来说明流式处理具体是如何操作的。

public static void peekForEach(){
Stream.of(1, 2, 3)
.peek(e -> log.info(String.valueOf(e)))
.forEach(e->log.info("forEach"+e));

这一次我们把toList方法替换成了forEach,通过具体的打印日志来看看到底发生了什么。

[main] INFO com.flydean.Main - 1
[main] INFO com.flydean.Main - forEach1
[main] INFO com.flydean.Main - 2
[main] INFO com.flydean.Main - forEach2
[main] INFO com.flydean.Main - 3
[main] INFO com.flydean.Main - forEach3

通过日志,我们可以看出,流式处理的流程是对应流中的每一个元素,分别经历了peek和forEach操作。而不是先把所有的元素都peek过后再进行forEach。

Stream的懒执行策略

之所有会有流式操作,就是因为可能要处理的数据比较多,无法一次性加载到内存中。

所以为了优化stream的链式调用的效率,stream提供了一个懒加载的策略。

什么是懒加载呢?

就是说stream的方法中,除了部分terminal operation之外,其他的都是intermediate operation.

比如count,toList这些就是terminal operation。当接受到这些方法的时候,整个stream链条就要执行了。

而peek和map这些操作就是intermediate operation。

intermediate operation的特点是立即返回,如果最后没有以terminal operation结束,intermediate operation实际上是不会执行的。

我们来看个具体的例子:

public static void peekLazy(){
Stream.of(1, 2, 3)
.peek(e -> log.info(String.valueOf(e)));

运行之后你会发现,什么输出都没有。

这表示peek中的逻辑并没有被调用,所以这种情况大家一定要注意。

peek为什么只被推荐在debug中使用

如果你阅读过peek的文档,你可能会发现peek是只被推荐在debug中使用的,为什么呢?

JDK中的原话是这样说的:

In cases where the stream implementation is able to optimize away the production of some or all the elements (such as with short-circuiting operations like findFirst, or in the example described in count), the action will not be invoked for those elements.

翻译过来的意思就是,因为stream的不同实现对实现方式进行了优化,所以不能够保证peek中的逻辑一定会被调用。

我们再来举个例子:

public static void peekNotExecute(){
Stream.of(1, 2, 3)
.peek(e -> log.info("peekNotExecute"+e))
.count();

这里的terminal operation是count,表示对stream中的元素进行统计。

因为peek方法中参数是一个Consumer,它不会对stream中元素的个数产生影响,所以最后的运行结果就是3。

peek中的日志输出并没有打印出来,表示peek没有被执行。

所以,我们在使用peek的时候,一定要注意peek方法是否会被优化。要不然就会成为一个隐藏很深的bug。

peek和map的区别

好了,讲到这里,大家应该对peek有了一个全面的认识了。但是stream中还有一个和peek类似的方法叫做map。他们有什么区别呢?

前面我们讲到了peek方法需要的参数是Consumer,而map方法需要的参数是一个Function:

Stream map(Function mapper);

Function也是一个FunctionalInterface,这个接口需要实现下面的方法:

R apply(T t);

可以看出apply方法实际上是有返回值的,这跟Consumer是不同的。所以一般来说map是用来修改stream中具体元素的。 而peek则没有这个功能。

peek方法接收一个Consumer的入参. 了解λ表达式的应该明白 Consumer的实现类应该只有一个方法,该方法返回类型为void. 它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素.

map方法接收一个Function作为入参. Function是有返回值的, 这就表示map对Stream中的元素的操作结果都会返回到Stream中去.

  • 要注意的是,peek对一个对象进行操作的时候,虽然对象不变,但是可以改变对象里面的值。

大家可以运行下面的例子:

public static void peekUnModified(){
Stream.of(1, 2, 3)
.peek(e -> e=e+1)
.forEach(e->log.info("peek unModified"+e));

public static void mapModified(){
Stream.of(1, 2, 3)
.map(e -> e=e+1)
.forEach(e->log.info("map modified"+e));
}

总结

以上就是对peek的总结啦,大家在使用的时候一定要注意存在的诸多陷阱。

本文的例子https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/peek-and-map/

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
不是山西!郭士强真正下家浮出水面,手握状元签期待冠军教头加盟

不是山西!郭士强真正下家浮出水面,手握状元签期待冠军教头加盟

开心体育站
2024-06-02 12:33:56
与“台独”划清界限,金门发表四大宣言,建“永久非军事区”

与“台独”划清界限,金门发表四大宣言,建“永久非军事区”

小波爱娱乐吖
2024-05-30 12:06:25
普京最大靠山出现,美国都傻眼了!中方:有人坐不住了

普京最大靠山出现,美国都傻眼了!中方:有人坐不住了

星辰故事屋
2024-05-30 17:43:41
天雷滚滚,星期六也不消停!昨日有12家公司收到了年报问询函

天雷滚滚,星期六也不消停!昨日有12家公司收到了年报问询函

惜别的海岸
2024-06-02 07:43:39
太强了!5月中国品牌汽车销量榜,比亚迪断崖式夺冠,理想第6名

太强了!5月中国品牌汽车销量榜,比亚迪断崖式夺冠,理想第6名

看看娱乐与体育
2024-06-02 12:02:08
北京下周阵雨雷雨频繁,这一时段雨势较强

北京下周阵雨雷雨频繁,这一时段雨势较强

北青网-北京青年报
2024-06-02 14:29:08
太夸张!皇马15座欧冠冠军,和英超打平,多于意甲、德甲、法甲

太夸张!皇马15座欧冠冠军,和英超打平,多于意甲、德甲、法甲

直播吧
2024-06-02 15:41:13
岁月不饶人!男单世界冠军2:3被淘汰,早田希娜3:1,国乒2人出局

岁月不饶人!男单世界冠军2:3被淘汰,早田希娜3:1,国乒2人出局

国乒二三事
2024-06-02 06:52:55
怒公开抄袭证据 阿沁宣战詹雯婷:把你告到底!

怒公开抄袭证据 阿沁宣战詹雯婷:把你告到底!

楚门记
2024-06-02 11:41:30
安帅:这座欧冠靠牺牲和能力赢得 如果克罗斯改变主意我们在等他

安帅:这座欧冠靠牺牲和能力赢得 如果克罗斯改变主意我们在等他

直播吧
2024-06-02 11:13:03
纪实男子用冰糖冒充毒品月售40公斤,称:抓我也没用,我又没犯罪

纪实男子用冰糖冒充毒品月售40公斤,称:抓我也没用,我又没犯罪

科学发掘
2024-06-01 19:22:41
历史魔咒,百年无解

历史魔咒,百年无解

我是历史其实挺有趣
2024-06-01 14:54:16
中国决战意大利,美波硬碰硬,巴西剑指八连胜,世联最新赛程

中国决战意大利,美波硬碰硬,巴西剑指八连胜,世联最新赛程

撒丁岛体育
2024-06-02 01:18:30
1.2万吨橘子皮被丢进了自然保护区,16年后,直接打了法官的脸

1.2万吨橘子皮被丢进了自然保护区,16年后,直接打了法官的脸

梦里大唐
2024-05-27 17:57:09
打麻将随身带这三样东西运气爆棚

打麻将随身带这三样东西运气爆棚

漫步风雨中
2024-05-30 13:39:16
京东回应“磨铁抵制京东”,否认站在行业对立面 京东App磨铁网店仍在正常销售

京东回应“磨铁抵制京东”,否认站在行业对立面 京东App磨铁网店仍在正常销售

红星新闻
2024-06-01 19:46:36
真正厉害的人,从来不解决问题

真正厉害的人,从来不解决问题

好笑娱乐君每一天
2024-05-28 18:59:12
穆里尼奥主教练生涯回顾:两夺欧冠,四获世界最佳俱乐部教练

穆里尼奥主教练生涯回顾:两夺欧冠,四获世界最佳俱乐部教练

懂球帝
2024-06-01 11:31:08
多特头号罪人:0射门1过人,恐被退回曼联!穆帅:都怪滕哈赫!

多特头号罪人:0射门1过人,恐被退回曼联!穆帅:都怪滕哈赫!

风过乡
2024-06-02 11:16:23
大陆中止ECFA关税减让后,台公布民调,称76%民众不认同九二共识

大陆中止ECFA关税减让后,台公布民调,称76%民众不认同九二共识

莫将离
2024-06-01 23:40:54
2024-06-02 17:12:49
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

成功着陆!嫦娥六号将开始月背“挖宝”

头条要闻

特朗普被判有罪后新动作:入驻TikTok 粉丝数已破24万

头条要闻

特朗普被判有罪后新动作:入驻TikTok 粉丝数已破24万

体育要闻

我已伤痕累累 却依然感动不了命运之神

娱乐要闻

白玉兰提名:胡歌、范伟争视帝

财经要闻

新造车5月销量: 小鹏乏力 问界暂"缺席"

汽车要闻

吉利银河E5 Flyme Auto智能座舱首发

态度原创

游戏
本地
数码
时尚
公开课

剑网3主策喊话逆水寒抵制抽卡(天赏):商战先自砍一刀?

本地新闻

食味印象|歙县限定!枇杷味儿的清甜初夏

数码要闻

苹果官网下架 OtterBox 配件,消息称国内代理商“不做了”

夏天适合40+姐姐的职场穿搭,时尚还高级,潮流女王就是你

公开课

近视只是视力差?小心并发症

无障碍浏览 进入关怀版