Jdk8新特性之Stream

339 阅读4分钟

Stream简介

Stream是Java8提供的一个新的API,它位于java.util.stream包下。Stream API提供了一种新的方式来对Java集合进行操作,这种操作方式极大的提高了码代码的生产力,让我们写出高效率、干净、简洁的代码。 我们可以将元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。

从上图可以很清晰的看到stream接口继承自BaseStream,其中IntStream, LongStream, DoubleStream对应三种基本类型的流操作,Stream对应所有剩余类型的stream视图。为不同数据类型设置不同stream接口,可以提高性能也可以增加特定接口函数。

Stream到底与集合有什么区别呢?Stream有以下几点特点:

  • 不存储数据

    流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。

  • 函数式编程

    流的操作不会修改数据源,例如filter不会将数据源中的数据删除。

  • 惰性执行

    流的很多操作如filter,map等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行。

  • 纯消费

    stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

Stream相关操作

Stream对象如何创建

Stream对象分为两种,一种串行的流对象,一种并行的流对象。除非显示地创建并行流,否则Java库中创建的都是串行流。

//创建串行流
Stream stream = Collection.stream();
Arrays.asList(1,2,3).stream();
//创建并行流
Stream parallelStream = Collection.parallelStream();
//可以使用BaseStream.sequential() 将流改为串行
//使用BaseStream.parallel()将流改为并行

构造一个数组信息,用于介绍Stream API的常用操作

class Employee {
    private Long empno; //员工号
    private String ename; //员工姓名
    private Integer salary; //薪水
    private Integer deptno; //所属部门号
    //......
}
Employee e1 = new Employee(1L, "admin1", 800, 20);
Employee e2 = new Employee(2L, "admin2", 1600, 30);
Employee e3 = new Employee(3L, "admin3", 1300, 30);
Employee e4 = new Employee(4L, "admin4", 2600, 10);
Employee e5 = new Employee(5L, "admin5", 1100, 20);

List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5);

forEach方法

forEach方法用于迭代stream流中的每一个元素

employees.stream().forEach(System.out::println);

执行结果:

Employee{empno=1, ename='admin1', salary=800, deptno=20}
Employee{empno=2, ename='admin2', salary=1600, deptno=30}
Employee{empno=3, ename='admin3', salary=1300, deptno=30}
Employee{empno=4, ename='admin4', salary=2600, deptno=10}
Employee{empno=5, ename='admin5', salary=1100, deptno=20}

map方法

map方法用于根据自定义的规则对stream流中的数据做一对一的映射

//获取所有员工的姓名
List<String> enames = employees.stream().map(employee -> employee.getEname()).collect(Collectors.toList());
enames.stream().forEach(System.out::println);

执行结果:

admin1
admin2
admin3
admin4
admin5
mapToInt/mapToLong/mapToDouble方法

这几个方法主要用来对stream流中的元素产生单个的统计结果

 //获取所有员工的薪水总和
int totalSalary = employees.stream().mapToInt(employee -> employee.getSalary()).sum();
System.out.println("薪水总和:" + totalSalary);

执行结果:

薪水总和:7400

filter方法

对Stream中的元素进行过滤操作,当设置条件返回true时返回相应元素。

//获取薪水超过1500的员工
List<Employee> filterEmp = employees.stream().filter(employee -> employee.getSalary()>1500).collect(Collectors.toList());
filterEmp.stream().forEach(System.out::println);

执行结果:

Employee{empno=2, ename='admin2', salary=1600, deptno=30}
Employee{empno=4, ename='admin4', salary=2450, deptno=10}

sorted方法

对Stream中元素按指定规则进行排序。

//按员工的薪水由低到高排序
List<Employee> sortedEmp = employees.stream().sorted(Comparator.comparing(Employee::getSalary)).collect(Collectors.toList());
sortedEmp.stream().forEach(System.out::println);

Collectors类

Collectors 类实现了很多归类操作,使你可以像在数据库中写SQL般丝滑,很爽。

//按员工所属部门号进行分类
Map<Integer, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(employee -> employee.getDeptno()));
for(Map.Entry<Integer, List<Employee>> entry : map.entrySet()) {
    System.out.println("key: " + entry.getKey() + "  value:" + entry.getValue());
}

System.out.println();

//获取员工姓名,用","进行拼接
String enameString = employees.stream().map(employee -> employee.getEname()).collect(Collectors.joining(","));
System.out.println(enameString);

执行结果:

key: 20  value:[Employee{empno=1, ename='admin1', salary=800, deptno=20}, Employee{empno=5, ename='admin5', salary=1100, deptno=20}]
key: 10  value:[Employee{empno=4, ename='admin4', salary=2600, deptno=10}]
key: 30  value:[Employee{empno=2, ename='admin2', salary=1600, deptno=30}, Employee{empno=3, ename='admin3', salary=1300, deptno=30}]

SMITH,ALLEN,WARD,CLARK,ADAMS

总结

Stream API提供了多个方法对集合进行映射、过滤、排序等操作,配合Collectors、Lambda表达式使用起来非常方便,会很大的简化代码,更快捷的进行开发。

非常重要的一点就是:Stream API中的方法并不会影响原始集合,只是通过流的方式得到我们想要的结果

有任何问题欢迎关注公众号【Hugh的白板】私信我,一起探讨,一起学习