Java Lambda从入门到精通九 认识流stream

Lambda中流什么是

流-stream可以粗略的理解为Java中的集合,但和集合还有一点区别。

流支持对数据集合进行各类操作,其能做的操作和集合类似,也是对一组数据进行操作,其特性更像是数据库的增删改查语句,例如我要查询班级中数学成绩大于90分的同学列表,SQL类似:

select * from 班级 where 数学 > 90

通过上面的语句,就能获取一个经过过滤的集合,流相当于持有一个全班的集合,然后进行数学分数过滤,最终得到数学成绩大于90的同学的一个新的集合,我们用集合可以完成,用SQL能够完成,用流同样能够完成,那么集合和流的具体区别在哪里呢?

流和集合的区别

1、集合与流之间的差异就在于什么时候进行计算

集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中;

流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。按需生成。这是一种生产者-消费者的关系。从另一个角度来说,流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值(用管理学的话说这就是需求驱动,甚至是实时制造)。流的整个特性总结就是:使用时才计算,按需计算。

2、集合是外部迭代,流是内部迭代

整个我们稍后从demo就能看到效果。

我们先构造一个Student类,如下:

package com.itzhimei.study.lambda.stream;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
@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;
}

接下来我们实现demo代码:

package com.itzhimei.study.lambda.stream;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class StreamTest1 {

    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);

        //集合过滤90分的
        List<Student> math90 = new ArrayList<>();
        for(Student s: list) {
            if(s.getMath()>90) {
                math90.add(s);
            }
        }
        System.out.println("======集合过滤90分的:======");
        for(Student s:math90) {
            System.out.println(s.toString());
        }

        //流过滤90分的
        List<Student> math90Stream = list.stream().filter(x -> x.getMath() > 90).collect(Collectors.toList());
        System.out.println("======流过滤90分的:======");
        math90Stream.forEach(System.out::println);
    }
}

在demo中,我们先for循环构建了10条数据,输出结果如下:

======初始化数据:======
Student(classId=null, no=1, name=小明, age=12, math=85.0, chinese=null, english=null, score=null)
Student(classId=null, no=2, name=小华, age=12, math=86.0, chinese=null, english=null, score=null)
Student(classId=null, no=3, name=小志, age=12, math=87.0, chinese=null, english=null, score=null)
Student(classId=null, no=4, name=小东, age=12, math=88.0, chinese=null, english=null, score=null)
Student(classId=null, no=5, name=小李, age=12, math=89.0, chinese=null, english=null, score=null)
Student(classId=null, no=6, name=小张, age=13, math=90.0, chinese=null, english=null, score=null)
Student(classId=null, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
Student(classId=null, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
Student(classId=null, no=9, name=小吴, age=13, math=93.0, chinese=null, english=null, score=null)
Student(classId=null, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)

然后我们使用集合来遍历里面的数据,找到每个学生数学成绩大于90,并放入到结果集合中,输出结果如下:

======集合过滤90分的:======
Student(classId=null, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
Student(classId=null, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
Student(classId=null, no=9, name=小吴, age=13, math=93.0, chinese=null, english=null, score=null)
Student(classId=null, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)

最后我们使用了本节要将的流的处理方式,来过滤数学成绩岛屿90的学生集合,输出如下:

======流过滤90分的:======
Student(classId=null, no=7, name=小王, age=13, math=91.0, chinese=null, english=null, score=null)
Student(classId=null, no=8, name=小周, age=13, math=92.0, chinese=null, english=null, score=null)
Student(classId=null, no=9, name=小吴, age=13, math=93.0, chinese=null, english=null, score=null)
Student(classId=null, no=10, name=小郑, age=13, math=94.0, chinese=null, english=null, score=null)

其中要关注的代码:

List<Student> math90Stream = list.stream().filter(x -> x.getMath() > 90).collect(Collectors.toList());

list后跟这stream()方法,是将一个集合转换为流,后面又跟了一个filter()方法,其作用就是过滤,从这里我们可以看到流的遍历操作,并没有显示的循环代码,这就是我们上面提到的,流是内部迭代

那么我们上面还提到了一个概念就是流是按需计算,也就是延时计算,只有真正用到的时候,计算才发生。

我们可以在上面的代码最后再添加一段代码:

//流的延时计算
        Stream<Student> studentStream = list.stream().filter(x -> x.getMath() > 90);
        List<Student> math90Stream2 = studentStream.collect(Collectors.toList());
        System.out.println("======[演示计算]流过滤90分的:======");
        math90Stream2.forEach(System.out::println);

你在运行的时候,可以在第二行的位置进行断点,执行时,你会发现第一行的studentStream对象,只是一个Stream对象,它的内部不存在我们需要的结果,那么这一行代码的作用仅仅是行为表达,还记得我们之前的章节说到的吗,Lambda的一个特性就是行为参数化,这里的第一行代码的作用就是表达的其行为,并没有实际产生任何结果数据,只有执行了第二行代码的collect(Collectors.toList())方法,才会进行过滤和计算,最终输出结果,这就是流的按需计算

总结:Java中lambda的流,我们需要关注两个概念,一个是流是内部迭代,另一个是流的按需计算