Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据,将要处理的元素集合看作一种流
流在管道中传输,并且可以在管道的节点上进行处理,比如筛选、排序、聚合等,在管道中经过中间操作的处理后,最后由最终操作得到前面处理的结果。
总而言之,流的使用一般包括三件事:
一个数据源(如集合)来执行一个查询;
一个中间操作链,形成一条流的流水线;
一个终端操作,执行流水线,并生成结果。
中间操作,比如
filter() 、 map() 、sorted()、skip()、peek
终端操作,比如
allMatch()、anyMatch()、noneMatch()、forEach()、collect()、count()
流有两大特点,流水线(流水线的操作可以看作对数据源进行数据库式查询)和内部迭代,使用Stream API 可以写出更简洁、易读、灵活和性能更好的代码
在 Java 8 中, 集合接口有两个方法来生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流
通过简单的例子熟悉stream的使用
创建一个User类并进行各种stream流操作
@Data
@AllArgsConstructor
public class User {
public Long id;
private String name;
private Integer age;
private Double grades;
private String sex;
/**
* 季度平时分
*/
private String usualGrades;
}
流操作
// 查询成绩及格的用户的姓名
List<String> nameList1 = users.stream().filter(x -> x.getGrades() > 60).map(User::getName).collect(Collectors.toList());
// 查询成绩前三(成绩相同,id较小的靠前)的姓名
List<String> nameList2 = users.stream().sorted(Comparator.comparingDouble(User::getGrades).reversed().thenComparingLong(User::getId))
.limit(3).map(User::getName).collect(Collectors.toList());
// 统计男生数量
long num = users.stream().filter(x -> x.getSex().equals("男")).count();
//统计全班的平时分总和
Integer collect = users.stream().map(x -> x.getUsualGrades().split(",")).flatMap(Arrays::stream).collect(Collectors.summingInt(x -> Integer.parseInt(x)));
// 查看班上是否一位叫小明的同学
boolean flag = users.stream().anyMatch(x -> x.getName().equals("小明"));
// 查看全班同学是否都以"小"开头
boolean flag2 = users.stream().allMatch(x -> x.getName().startsWith("小"));
// 打印所有信息
users.stream().forEach(System.out::println);
通过上述操作,也简单了解一些流的操作
- filter 接受Lambda,从流中排除某些元素或者提取某些元素
- map 接受一个Lambda,将元素转换成其他形式或提取元素
- collect 将流转换为其他形式
- sorted 将某些元素进行排序
- limit 截断流,使其元素不超过给定数量
- flatMap 将每个映射流的内容分裂成多个同样的流(一转多,比如:上述实例,每个同学有一个季度平时分【有三个分组成】,现在把他们都提取出来,变成一个个的分数)
- anyMatch 匹配上任何一个则返回 Boolean
- allMatch 匹配所有的元素则返回 Boolean
- findAny 查找任何一个就返回 Optional
- findFirst 查找到第一个就返回 Optional
- forEach 遍历所有信息
还有挺多功能强大的流操作值得我们去发现,在上述操作,发现大部分操作都是接受一个Laombda表达式的语句,点击进入看源码我们能发现传入的是一个函数式接口,比如filter,传入是Predicate接口,其中只有一个方法test,传入一个参数,返回boolean类型
函数式接口
一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口,可以被隐式转换为 lambda 表达式。
常用的函数式接口:
- Consumer 顾名思义,Consumer的意思就是消费,默认方法是 void accept(T t)【代表了接受一个输入参数并且无返回的操作】,流操作forEach() 就用到
- Function 功能或者函数 ,默认方法是 R apply(T t)【接受一个输入参数,返回一个结果】 ,在流操作map()用到
- Predicate 断言,默认方法是boolean test(T t)【接受一个输入参数,返回一个布尔值结果】,流操作filter() 用到
- Supplier 提供、供应,默认方法是T get()【无参数,返回一个结果】
- BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
- BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
- IntConsumer 接受一个int类型的输入参数,无返回值
- IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果
- ToIntFunction 接受一个输入参数,返回一个int类型结果。