美文网首页
Java Stream peek vs. map

Java Stream peek vs. map

作者: 大哥你先走 | 来源:发表于2020-04-22 00:32 被阅读0次

    为什么要写本文

    在之前的一篇文章Java 8 Streams peek API中我们介绍了一些使用Java Stream peek函数的样例,读者对`peek 函数的使用提出很多建设性的建议,作者受益良多。与此同时也有小伙伴对peek和map两个函数之间的差异感到困惑,尤其涉及修改Stream中的元素时。考虑到之前的文章没有详细的解释清楚两个函数之间的区别,故此通过本文尝试做出一些有利于大家学习使用两个函数的解释,如有不足之处,恳请及时反馈,共同学习共同进步。

    peek和map的异同之处

    • 两个函数都是中间操作,都非常的‘懒’,没有对Stream的终止操作,两个函数都不会工作。
    • peek函数的存在仅仅是为了debug,而map是Stream的一个核心函数,两个函数的地位不同。
    • 两个函数的返回值都是一个新的Stream,但是两个函数的参数(peek是Consumer,map是Function)起作用的时机不同。map的Function在生成新的Stream之前被执行,新Stream中的元素是上游Stream中元素经Function作用后的值。peek函数的Consumer工作在生成Stream之后,下一节详细讲解两个函数执行时机。

    一个有代表性的样例

    以下面一段函数(来自peek函数的官方注释)为例解释peek和map两个函数工作机制的不同:

    List<String> list = 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());
    System.out.println(list);
    

    输出如下:

    Filtered value: three
    Mapped value: THREE
    Filtered value: four
    Mapped value: FOUR
    [THREE, FOUR]
    

    工作示意图如下:

    stream.png

    工作流程概述:

    1、初始Stream包含四个字符串:one,two,three和four

    2、Stream遇到的第一个中间操作是filter,filter的谓词是只保留长度大于3的字符串,经过谓词过滤后filter返回是一个仅包含两个字符串three和four的Stream1,谓词工作在Stream1生成之前。

    3、Stream遇到的第二个中间操作是peek,peek的Consumer是打印Stream中的字符串,peek直接生成一个和上游Stream1包含相同元素的Stream2,peek函数的Consumer工作在生成Stream2之后。

    4、Stream遇到的第三个中间操作是map,map的Function将字符串转换为全大写,Function作用于上游Stream2的每一个元素,并生成新的Stream3。

    5、Stream遇到的第四个中间操作是peek,peek的Consumer依然只是打印Stream4中的字符串,Consumer依然工作在Stream4生成之后。

    peek和map修改Stream的元素

    map函数对Stream中元素执行的是映射操作,会以新的元素(map的结果)填充新的Stream,严格的讲map不是修改原来的元素。peek只能消费Stream中的元素,是否可以更该Stream中的元素,取决于Stream中的元素是否是不可变对象。如果是不可变对象,则不可修改Stream中的元素;如果是可变对象,则可以修改对象的值,但是无法修改对象的引用。

    • 不可变对象场景:

    适当的修改上面的样例:

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

    输出如下:

    three-three
    Mapped value: THREE
    four-four
    Mapped value: FOUR
    [THREE, FOUR]
    

    由输出结果可知peek并没有修改Stream的元素,list的值依然是:[THREE, FOUR]

    • 可变对象

    定义一个简单的Java对象Company

    class Company {
        private String name;
        private int age;
        // 省略构造函数,getter/setter和toString方法
    }
    

    样例代码:

    Company apple = new Company("apple", 44);
    Company huawei = new Company("huawei", 33);
    Company qualcomm = new Company("Qualcomm ", 35);
    List<Company> list = Stream.of(apple, huawei, qualcomm)
            .filter(company -> company.getAge() < 35)
            .peek(company -> company.setAge(company.getAge() - 10))
            .map(company -> new Company(company.getName().toUpperCase(),company.getAge()))
            .peek(e -> System.out.println("Mapped value: " + e))
            .collect(Collectors.toList());
    System.out.println(list);
    

    上面的代码输出的结果是什么?不妨花费几分钟的时间思考以下。

    🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔

    输出的结果如下:

    Mapped value: Person{name='HUAWEI', age=23}
    [Person{name='HUAWEI', age=23}]
    

    你计算的结果和预期一致吗?

    对于peek和map函数你理解了吗,如果我还是没有讲清楚,欢迎留言交流。

    相关文章

      网友评论

          本文标题:Java Stream peek vs. map

          本文链接:https://www.haomeiwen.com/subject/ilpvihtx.html