设为首页 加入收藏

TOP

再聊Java Stream的一些实战技能与注意点(一)
2023-09-23 15:44:34 】 浏览:249
Tags:再聊 Java Stream 能与注

大家好,又见面了。

在此前我的文章中,曾分2篇详细探讨了下JAVA中Stream流的相关操作,2篇文章收获了累计 10w+阅读、2k+点赞以及 5k+收藏的记录。能够得到众多小伙伴的认可,是技术分享过程中最开心的事情。

不少小伙伴在评论中提出了一些的疑问或自己的独到见解,也在评论区中进行了热烈的互动讨论。梳理了下相关评论内容,针对此前文章中没有提及的一些典型讨论点拿出来聊一聊,也是作为对此前两篇Java Stream相关文章内容的补充完善。

Stream处理时列表到底循环了多少次

看下面这段Stream使用的常见场景:

Stream.of(17, 22, 35, 12, 37)
        .filter(age -> age > 18)
        .filter(age -> age < 35)
        .map(age -> age + "岁")
        .collect(Collectors.toList());

在这段代码里面,同时有2个 filter操作和1个 map操作以及1个 collect操作,那么这段代码执行的时候,究竟是对这个list执行了几次循环操作呢?是每一个Stream步骤都会进行一次遍历操作吗?为了验证这个问题,我们将上述代码改写一下,打印下每个步骤的结果:

        List<String> ages = Stream.of(17,22,35,12,37)
                .filter(age -> {
                    System.out.println("filter1 处理:" + age);
                    return age > 18;
                })
                .filter(age -> {
                    System.out.println("filter2 处理:" + age);
                    return age < 35;
                })
                .map(age -> {
                    System.out.println("map 处理:" + age);
                    return age + "岁";
                })
                .collect(Collectors.toList());

先执行,得到如下的执行结果。其实结果已经很明显的可以看出,stream流处理的时候,是对列表进行了一次循环,然后顺序的执行给定的stream执行语句。

按照上述输出的结果,可以看出其处理的过程可以等价于如下的常规写法:

        List<Integer> ages = Arrays.asList(17,22,35,12,37);
        List<String> results = new ArrayList<>();
        for (Integer age : ages) {
            if (age > 18) {
                if (age < 35) {
                    results.add(age + "岁");
                }
            }
        }
        System.out.println(results);

所以,Stream并不会去遍历很多次。其实上述逻辑也符合Stream 流水线加工的整体模式,试想一下,一条流水线上分环节加工一件商品,同一件产品也不会在流水线上加工2次的吧~

img

Stream究竟是让代码更易读还是更难懂

Java8引入了 Lambda函数式接口Stream等新鲜内容以来,针对使用Stream或Lambda语法究竟是让代码更易懂还是更复杂的争议,一直就没有停止过。有的同学会觉得Stream语法的方式,一眼就可以看出业务逻辑本身的含义,也有一些同学认为使用了Stream之后代码的可读性降低了很多。

其实,这是个人编码模式与理念上的不同感知而已。Stream主打的就是让代码更聚焦自身逻辑,省去其余繁文缛节对代码逻辑的干扰,整体编码上会更加的简洁。但是刚接触的时候,难免会需要一定的适应期。技术总是在不断迭代、不断拥抱新技术、不去刻意排斥新技术,或许是一个更好的选项。

那么,话说回来,如何让自己能够一眼看懂Stream代码、感受到Stream的简洁之美呢?分享个人的一个经验:

  1. 先了解几个常见的Stream的api的功能含义(Stream的API封装的很优秀,很多都是字面意义就可以理解)
  2. 改变意识,聚焦纯粹的业务逻辑本身,不要在乎具体写法细节

下面举了个例子,如何用上述的2条方法,快速的让自己理解一段Stream代码表达的意思。

那么上面这段代码的含义就是,先根据员工子公司过滤所有上海公司的人员,再获取员工工资最高的那个人信息。怎么样?按照这个方法,是不是可以发现,Stream的方式,确实更加容易理解了呢~

在IDEA中debug调试Stream代码段

技术分享其实是一个双向的过程,分享的同时,也是自我学习与提升的机会,除了可以梳理发现一些自己之前忽略的知识点并加以巩固,还可以在互动的时候get到新的技能。

比如,我在此前的 Java Stream介绍的文章中,有提过基于Stream进行编码的时候会导致代码 debug调试的时候会比较困难,尤其是那种只有一行Lambda表达式的情况(因为如果代码逻辑多行编写的时候,可以在代码块内部打断点,这样其实也可以进行debug调试)。

关于这一点,很多小伙伴也有相同的感受,比如下面这个评论:

你以为这就结束了?接下来一个小伙伴的提示,“震惊”了众人!纳尼?原来Stream代码段也是可以debug单步调试的?

跟踪Stream中单步处理过程的操作入口按钮长这样:

并且,另一个小伙伴补充说这是IDEA2019.03版本开始有的功能:

嗯?难怪呢,我一直用的2019.02版本的,所以才没用上这个功能(强行给自己找了个台阶、哈哈哈)。于是,我悄悄的将自己的idea升级到了最新的2023.02版本(PS:新版本的UI挺好看,就是bug贼多)。好啦,言归正传,那么究竟应该如何利用IDEA来实现单步DEBUG呢?一一起来感受下吧。

在代码行前面添加断点的时候,如果要打断点的这行代码里面包含Stream中间方法map\filter\sort之类的)的时候,会提示让选择断点的具体类型

一共有三种类型断点可供选择:

  • Line:断点打在这一行上,不会进入到具体的Stream执行函数块中
  • Lambda:代码打在内部的lambda代码块上
  • Line and Lambda:代码走到这行或者执行这一行具体的函数块内容的时候,都会进入断点

下面这个图可以更清晰的解释清楚上述三者的区别。一般来说,我们debug的时候,更多的是关注自身的业务具体逻辑,而不会过多去关注Stream执行框架的运转逻辑,所以大部分情况下,我们选择第二个Lambda选项即可

按照上面所述,我们在代码行前面添加一个Lambda类型断点,然后debug模式启动程序执行,等到断点进入的时候便可以正常的进行debug并查看内部的处理逻辑了。

如果遇到图中这种只有一行的lambda形式代码,想要看下返回值到底是什么的,可以选中执行的片段,然后 ALT+F8打开eva luate界面(或者右键选择 eva luate Expression),点击 eva ludate按钮执行查看具体结果。

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇什么是方法重载?返回值算重载吗? 下一篇ArrayList/MySQL数据批量写入Exce..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目