魔幻数据穿梭之Stream API
在我们做项目时,时常需要对数据进行整合、筛选等操作,每次用for循环之后再进行判断等等操作相信大家一定感到厌烦了,现在Java8新退出的Lamda表达式搭配Steam API可以完美的进行数据处理,完成在数据之间穿梭的功能。
Stream故名思意就是流的意思,而这个流与IO流中还有些许的不同,我们是通过对数据流流水线操作后得到想要的数据。
Stream API之创建流
创建Stream流有多种方式,可以通过集合的实例方法 .stream() 创建、通过数组工具类的静态方法 Arrays.stream() 创建、通过Stream类的静态方法 Stream.of() 创建,还可以通过 Stream.iterate() 创建流;其中最后一种是创建无限流,也就是一直运行,没有结束。
集合实例方法创建
通过集合实例方法创建流是我们一般使用的形式。
// 1. 可以通过Collection系列集合提供的 stream() (串行流)或 parallelStream()(并行流) 可以获取流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
数组工具类的静态方法创建流
// 2. 可以通过 Arrays 中的静态方法 stream() 获取数组流
Employee[] emps = new Employee[10];
Stream<Employee> stream1 = Arrays.stream(emps);
Stream静态方法 of()
// 3. 通过 Stream 中的静态方法 of()
Stream<String> stream2 = Stream.of("aa", "bb", "vv");
Stream静态方法 iterate()与 generate()
Stream的iterate()创建的是无限流,何为无限流呢,就是没有终止操作就永不停止。
// 4. 创建无限流
// 迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.limit(10).forEach(System.out::println);
// 生成
Stream.generate(() -> (Math.random()))
.limit(5)
.forEach(System.out::println);
Stream API的中间操作
Stream API的中间操作有很多,如:筛选、规约、映射、排序等。
Stream的中间操作有一个鲜明的性质:在没有执行终端操作时,中间操作都不执行;当执行终端操作时,将流水线的形式执行终端操作。
筛选与切片
Stream API中提供了对数据流筛选和切片的方法,分别是:
filter —— 接收Lamda,从流中排除元素。
limit —— 截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,赶回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补。
distinct —— 去重,通过流所生成元素的hanshcode()和equals()取出重复元素。
@Test
public void test5(){
emps.stream()
.distinct()
.forEach(System.out::println);
}
@Test
public void test4() {
emps.stream()
.filter((e) -> e.getSalary()<5000)
.skip(2)
.forEach(System.out::println);
}
@Test
public void test3() {
emps.stream()
.filter((e) -> {
System.out.println("短路!");
return e.getSalary() < 5000;
})
.limit(2)
.forEach(System.out::println);
}
// 内部迭代:迭代操作有 Stream API 完成
@Test
public void test1() {
// 中间操作:不会执行任何处理
Stream<Employee> s = emps.stream()
.filter((e) -> {
System.out.println("Stream API 的中间操作");
return e.getAge() > 35;
});
// 终止操作:一次性执行全部内容,即 “惰性求值”
s.forEach(System.out::println);
}
映射
Strame API中提供映射功能的API有两个,分别是map与flatMap。
map:接受一个函数作为参数,流中每一个元素都会执行这个函数并以函数的返回值形式重新组成流。
flatMap:接收一个函数作为参数,将流中的每个值作为另一个流,然后把所有的流连成一个流。
其中map与flatMap的区别在于无论接受的函数返回的值为什么类型,flapMap都会把其中的各个元素整理为一个一个的流,最后把所有的流都连成一个流,而map只是将返回值放入流中。
例如:当接收的函数返回一个流时,map最后组合成的是流中嵌套流,而flapMap只返回最后所有元素串在一起的流。
@Test
public void test6() {
List<String> list = Arrays.asList("aaa", "bb", "as", "ee");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
System.out.println("=======================");
emps.stream()
.map(Employee::getName)
.forEach(System.out::println);
System.out.println("=======================");
Stream<Stream<Character>> stream = list.stream()
.map(TestStreamAPI2::filterCharacter);
stream.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("=======================");
list.stream()
.flatMap(TestStreamAPI2::filterCharacter)
.forEach(System.out::println);
}
排序
Stream API提供的排序方法只有一个,就是sorted(),但它有有参和无参两种方法;
sorted() 自然排序
sorted(Comparator com) —— 定制排序
@Test
public void test7() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("=================");
emps.stream()
.sorted((x,y) -> {
if (x.getAge().equals(y.getAge())){
return x.getName().compareTo(y.getName());
}else {
return x.getAge().compareTo(y.getAge());
}
}).forEach(System.out::println);
}
终止操作
Stream提供了多种的终止操作API,可以和中间操作完美配合,处理数据。
查找与匹配
allMatch —— 检查是否匹配所有元素
anyMatch —— 检查是否至少匹配一个元素
noneMatch —— 检查是否没有匹配所有元素
findFirst —— 返回第一个元素
findAny —— 返回当前流中所有的元素
count —— 返回流中元素的总个数
max —— 返回流中最大值
min —— 返回流中最小值
@Test
public void test2() {
long count = emps.stream()
.count();
System.out.println(count);
System.out.println("============================");
Optional<Employee> max = emps.stream()
.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max.get());
System.out.println("============================");
// Optional<Employee> min = emps.stream()
// .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
// System.out.println(min.get().getSalary());
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.min(Double::compareTo);
System.out.println(min.get());
}
@Test
public void test1() {
boolean b = emps.stream()
.allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b);
System.out.println("===========================");
boolean b1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b1);
System.out.println("===========================");
boolean b2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
System.out.println(b2);
System.out.println("===========================");
Optional<Employee> op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());
System.out.println("===========================");
Optional<Employee> any = emps.stream()
.filter((e) -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(any.get());
}
归约
reduce(T identity, BinaryOprator) / reduce(BinaryOperator) —— 可以将流中的元素反复结合起来,得到一个值。
本质理解起来就是递归。
@Test
public void test3() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 求数组中的个数, 0 为初始值
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("=================================");
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
收集
因为工作中对集合操作的最多,所以收集的操作方式很常用。
collect —— 将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法。
@Test
public void test10() {
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(str);
}
@Test
public void test9() {
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
// 分区
@Test
public void test8() {
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
System.out.println(map);
}
// 多级分组
@Test
public void test7() {
Map<Employee.Status, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() < 30) {
return "青年";
} else if (e.getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
System.out.println(mapMap);
}
// 分组
@Test
public void test6() {
Map<Employee.Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
@Test
public void test5() {
// 总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
// 平均值
Double avage = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avage);
// 总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
// 最大值
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1, e2)));
System.out.println(max.get());
// 最小值
Optional<Double> min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compareTo));
System.out.println(min);
}
@Test
public void test4() {
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println(list.toString());
HashSet<String> collect = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
collect.forEach(System.out::println);
}