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 nameList1 = users.stream().filter(x -> x.getGrades() > 60).map(User::getName).collect(Collectors.toList());
// 查询成绩前三(成绩相同,id较小的靠前)的姓名
List 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 代表了一个接受两个输入参数的操作,并且不返回任何结果
BiFunction 代表了一个接受两个输入参数的方法,并且返回一个结果
IntConsumer 接受一个int类型的输入参数,无返回值
IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果
ToIntFunction 接受一个输入参数,返回一个int类型结果。