Java Lambda从入门到精通十 流的操作

流的操作分为两种:中间操作和终端操作。

中间操作:可以连起来的操作,中间操作会返回另一个流,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理,因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。

终端操作:关闭流的操作,终端操作会从流的流水线生成结果。生成的结果是任何不是流的值,比如List、Integer等。

我们以代码为例进行讲解,demo还是基于上一节的Student继续演示,上代码:

@Data
@ToString
@Builder
public class Student {

    //班级id
    private Integer classId;
    //学号
    private Integer no;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //数学
    private Double math;
    //语文
    private Double chinese;
    //英语
    private Double english;
    //总分
    private Double score;
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest2 {

    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        String[] names = {"小明","小华","小志","小东","小李","小张","小王","小周","小吴","小郑"};
        for(int i=0; i<10; i++) {
            Student student = Student.builder()
                    .name(names[i])
                    .age(12+i/5)
                    .no(i+1)
                    .math(85d+i).build();
            list.add(student);
        }
        //初始化数据
        System.out.println("======初始化数据:======");
        list.forEach(System.out::println);

        //流的流水线操作
        System.out.println("======流的流水线操作:======");
        List<String> std1 = list.stream().filter(x -> x.getMath() > 90d)
                .map(Student::getName)
                .limit(2)
                .collect(Collectors.toList());
        std1.forEach(System.out::println);

        //流的中间操作详解,实际代码不推荐这样写
        System.out.println("======流的中间操作详解:======");
        List<String> std2 = list.stream().filter(x -> {
            System.out.println("filter:"+x);
            return x.getMath() > 90d;
        })
                .map(x-> {
                    System.out.println("map:"+x);
                    return x.getName();
                })
                .limit(2)
                .collect(Collectors.toList());
    }
}

第一部分还是新建了10条演示数据。

        //流的流水线操作
        System.out.println("======流的流水线操作:======");
        List<String> std1 = list.stream().filter(x -> x.getMath() > 90d)
                .map(Student::getName)
                .limit(2)
                .collect(Collectors.toList());
        std1.forEach(System.out::println);

上面的第二行代码,除了最后的collect()方法,中间的filter()、map()、limit()方法,都是流的中间操作方法。collect()方法方法则是流的终端操作方法。

上面代码输出结果:

======流的流水线操作:======
小王
小周

输出了两条结果,因为使用了limit,作用就是取前两条。

我们将上面的代码继续改造一下,如下:

        //流的中间操作详解,实际代码不推荐这样写
        System.out.println("======流的中间操作详解:======");
        List<String> std2 = list.stream().filter(x -> {
            System.out.println("filter:"+x);
            return x.getMath() > 90d;
        })
                .map(x-> {
                    System.out.println("map:"+x);
                    return x.getName();
                })
                .limit(2)
                .collect(Collectors.toList());

这段代码同上一段代码的区别就是分别在filter方法和map方法中加入了一行System.out.println语句,用于打印方法执行时的内容,你会看到一些有趣的结果,输出如下:

======流的中间操作详解:======
filter:Student(classId=null, no=1, name=小明, age=12, math=85.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=2, name=小华, age=12, math=86.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=3, name=小志, age=12, math=87.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=4, name=小东, age=12, math=88.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=5, name=小李, age=12, math=89.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=6, name=小张, age=13, math=90.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
map:Student(classId=null, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
filter:Student(classId=null, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
map:Student(classId=null, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)

我们分析一下打印结果,filter打印了8行,map打印了两行,而且最后的4行打印内容是filter和map相连打印的,从日志输出你就能推断出,filter和map被合并执行了,并不是我们认为的执行完全部filter,再执行map,而且最后的limit也是短路执行,发现满足2两数据,立刻停止继续执行,输出结果。

总结:流的操作包含一个数据源,一组流的中间操作和一个终端操作。