Stream
流(Stream)
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算”!
注意:
1)Stream 不会自己存储元素
2)Stream 不会改变源对象。相反,Stream会返回一个持有结果的新Stream
3)Stream 操作是延迟执行的。这意味Stream会等到需要结果的时候才执行
一、Stream的三个操作步骤
1. 创建Stream
2. 中间操作
3. 终止操作(终端操作)
二、创建Stream
1. 通过Collection 系列集合提供的stream() 或 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
2. 通过Arrays 中的静态方法stream() 获取数组流
Employee[] emps = new Employee[10];
Stream<Employee> stream = Arrays.stream(emps);
3. 通过Stream 类中的静态方法of()
Stream<String> stream = Stream.of("aa", "bb", "cc");
4. 创建无限流
1)迭代
Stream<Integer> stream = Stream.iterate(0, x -> x+2);
stream.limit(10).forEach(System.out::println);
2) 生成
Stream.generate(() -> Math.random()).limit(5).forEach(System.out.println);
二、中间操作
多个中间操作可以连接形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”(延迟加载)。
List<Employee> employees = Arrays.asList(new Employee()...);
(一) 筛选与切片:
1. fileter —— 接受Lambda,从流中排除某些元素
内部迭代:迭代操作由Stream API完成
中间操作(不执行任何操作):
Stream<Employee> stream = employees.stream().filter(e -> e.getAge > 35);
Stream<Employee> stream = employees.stream().filter(e -> {
System.out.println("执行中间操作"); // 中间操作在没有终止操作的情况下不会执行
return e.getAge > 35;
});
终止操作(一次性执行全部内容,即“惰性求值”):
stream.forEach(System.out::println);
外部迭代:
Iterator<Employee> it = employees.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
2. limit —— 截断流,使其元素不超过给定数量
employees.stream()
.filter(e -> e.getSalary() > 5000)
.limit(2)
.forEach(System.out::println);
employees.stream()
.fileter(e -> {
System.out.println("短路"); // || 满足了就不执行
return e.getSalary() > 5000;
}).limit(2).forEach(System.out::println);
3. skip(n) —— 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
employees.stream().filter(e -> e.getSalary() > 5000).skip(2).forEach(System.out::println);
4. distinct —— 筛选,通过流所生成的元素的hashCode() 和equals() 去除重复元素
employees.stream().filter(e -> e.getSalary() > 5000).skip(2).distinct().forEach(System.out:println);
(二) 映射
List<String> list = Arrays.asList("aaa", "bbb",...);
1. map —— 接收Lambda, 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
employees.stream().map(Employee::getName).forEach(System.out::println);
2. flatMap —— 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
有如下方法:
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for(Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
// TestStream : 方法filterCharacter对应的类名
Stream<Stream<Character>> stream = list.stream().map(TestStream::filterCharacter);
stream.forEach(sm -> {
sm.forEach(System.out::println);
});
改用flatMap:
// 与add()、addAll()两个方法有异曲同工之妙
Stream<Character> sm = list.stream().flatMap(TeatStream::filterCharacter);
sm.forEach(System.out::println);
(三) 排序
1. sorted() —— 自然排序(Comparable)
list.stream().sorted().forEach(System.out::println);
2. sorted(Comparator com) —— 定制排序(Comparator)
employees.stream().sorted((e1, e2) -> {
if(e1.getAge().equals(e2.getAge())) {
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
三、终止操作
(一) 查找与匹配
1. allMatch —— 检查是否匹配所有元素
boolean b = employees.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));
2. anyMatch —— 检查是否匹配至少一个元素
boolean b =employees.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));
3. noneMatch —— 检查是否没有匹配所有元素
boolean b =employees.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));
4. findFirst —— 返回第一个元素
Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
op.orElse(other); // 若op中为空,可有一个替代的对象other(解决空指针异常)
op.get();
5. findAny —— 返回当前流中的任意元素
Optional<Employee> op = employees.stream().filter(e -> e.getStatus().equals(Status.FREE)).findAny(); // 获取串行流 单个线程找
Optional<Employee> op = employees.parallelStream().filter(e -> e.getStatus().equals(Status.FREE)).findAny(); // 获取并行流 多个线程同时找
6. count —— 返回流中的元素总个数
Long count = employees.stream().count();
7. max —— 返回流中的最大值
Optional<Employee> op = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
8. min —— 返回流中的最小值
Optional<Double> op = employees.stream().map(Employee::getSalary).min(Double::compare);
(二) 归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) —— 可以将流中元素反复结合起来,得到一个值
// 0 起始值 后面是个function 累计值 0首先作为x , list中的第一个值作为y,将和作为x继续运算 直到结束
1. Integer sum = list.stream().reduce(0, (x, y) -> x + y);
// 获取所有员工的工资总和
// sum是Double 中的静态方法 上面1.的reduce有起始值0 不可能为空 所以直接用Integer;而员工工资没有起始值,是有可能为空,所有返回optional 避免空指针
2. Optional<Double> op = employees.stream().map(Employee::getSalary()).reduce(Double::sum);
注:map和reduce的连接通常称为map-reduce模式,因Google用它进行网络搜索而出名。
(三)收集
collect——将流转换为其他形式。接受一个Collector接口的实现,用于给stream中元素做汇总的方法
List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
HashSet<String> hs = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
1. 总数
Long count = employees.stream().collect(Collectors.counting());
2. 平均值
Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
3. 总和
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
4. 最大值
Optional<Employee> op = employees.stream().collect(Collectors.maxBy(e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
5. 最小值
Optional<Double> op = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));
6. 分组
Map<Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
7. 多级分组
// 先按状态分组,再按年龄分组
Map<Status, Map<String, List<Employee>>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {
if (((Employee) e).getAge() <= 35) {
return "青年";
} else if (((Employee) e).getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
8. 分区(分片)
Map<boolean, List<Employee>> map =employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
9. 直接求和,求平均值,求最大值 非常方便
DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
10. 连接字符串
// 连接名字
String str = employees.stream().map(Employee::getName).collect(Collectors.joining());
// ,表示分隔符;===表示开头连接符;+++表示结尾连接符
String str = employees.stream().map(Employee::getName).collect(Collectors.joining(",", “===", "+++"))
浙公网安备 33010602011771号