Java8 关于stream.foreach()和stream.peek()的区别解析


该思考来源于日常工作中,特记此心得。

思考:如何快速将list中的每个item内部属性值改变并进行其他流体操作呢?

下面做个测试:如何先在list中统一改变某属性的值,然后再根据某个属性取出该属性值最小的对象

1:随便新建一个测试bean:

 1 package com.dev.model;
 2 
 3 import javax.persistence.*;
 4 
 5 public class Aopi {
 6     /**
 7      * id
 8      */
 9     @Id
10     @GeneratedValue(strategy = GenerationType.IDENTITY)
11     private Integer id;
12 
13     /**
14      * 姓名
15      */
16     private String name;
17 
18     /**
19      * 年龄
20      */
21     private Integer age;
22 
23     /**
24      * 获取id
25      *
26      * @return id - id
27      */
28     public Integer getId() {
29         return id;
30     }
31 
32     /**
33      * 设置id
34      *
35      * @param id id
36      */
37     public void setId(Integer id) {
38         this.id = id;
39     }
40 
41     /**
42      * 获取姓名
43      *
44      * @return name - 姓名
45      */
46     public String getName() {
47         return name;
48     }
49 
50     /**
51      * 设置姓名
52      *
53      * @param name 姓名
54      */
55     public void setName(String name) {
56         this.name = name;
57     }
58 
59     /**
60      * 获取年龄
61      *
62      * @return age - 年龄
63      */
64     public Integer getAge() {
65         return age;
66     }
67 
68     /**
69      * 设置年龄
70      *
71      * @param age 年龄
72      */
73     public void setAge(Integer age) {
74         this.age = age;
75     }
76 
77     public Aopi(String name, Integer age) {
78         this.name = name;
79         this.age = age;
80     }
81 
82     public Aopi() {
83     }
84 
85     @Override
86     public String toString() {
87         return "Aopi{" +
88                 "id=" + id +
89                 ", name='" + name + '\'' +
90                 ", age=" + age +
91                 '}';
92     }
93 }

2:新建一个单元测试:

    @Test
    public void test01() {
        List aopiList = Lists.newArrayList();

        Aopi aopi = new Aopi("1", 1);
        Aopi aop2 = new Aopi("2", 2);
        Aopi aop3 = new Aopi("3", 3);
        Aopi aop4 = new Aopi("4", 4);

        aopiList.addAll(Arrays.asList(aopi, aop2, aop3, aop4));

        //第一种方式
        aopiList.forEach(item -> item.setName(item.getName() + "_test"));
        System.out.println(
                aopiList.stream().min((o1, o2) -> {
                    if (Objects.equals(o1.getAge(), o2.getAge()))
                        return 0;
                    return o1.getAge() > o2.getAge() ? 1 : -1;
                }).get().toString()
        );

        System.out.println("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");

        //第二种方式
        System.out.println(
                aopiList.stream().peek(item -> item.setName(item.getName() + "_test")).min((o1, o2) -> {
                    if (Objects.equals(o1.getAge(), o2.getAge()))
                        return 0;
                    return o1.getAge() > o2.getAge() ? 1 : -1;
                }).get().toString()
        );

    }

notice1:测试第一种方式注释掉第二种,反之亦如此

notice2:list.stream().foreach  ->  list.foreach()

3:看测试结果:

第一种测试结果:

第二种测试结果:

结论:

(1):使用stream.foreach也可以更改list中的每个item的内部属性值等等,但是要进行“二次流处理”,才能得到list中最小的item(根据age筛选)

(2):stream.peek比stream.foreach()可以跟直接拿到最小的item(根据age筛选)

原因:

(1):stream.foreach的操作是void的,除了更改属性值还可以进行其他操作等。因此要做“二次流处理”。

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

(1):stream.peek的操作是返回一个新的stream的,且设计的初衷是用来debug调试的,因此使用steam.peek()必须对流进行一次处理再产生一个新的stream。

    /**
     * Returns a stream consisting of the elements of this stream, additionally
     * performing the provided action on each element as elements are consumed
     * from the resulting stream.
     *
     * 

This is an intermediate * operation. * *

For parallel stream pipelines, the action may be called at * whatever time and in whatever thread the element is made available by the * upstream operation. If the action modifies shared state, * it is responsible for providing the required synchronization. * * @apiNote This method exists mainly to support debugging, where you want * to see the elements as they flow past a certain point in a pipeline: *

{@code
     *     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());
     * }
* *
@param action a * non-interfering action to perform on the elements as * they are consumed from the stream * @return the new stream */ Stream peek(Consumer<? super T> action);