流处理
基础概念
流
流处理是对运动中的数据的处理,在生成或接收数据时直接计算数据。应用程序中分析和查询不断存在,数据不断地流经它们。在从流中接收到事件时,流处理应用程序对该事件作出反应。
如果我们使用传统的循环迭代方式对数据集进行复杂计算,常常会带来两个弊端:
- 迭代次数多,迭代次数跟函数调用的次数相等。
- 频繁产生中间结果,存储开销无法接受。
流处理可以立即对事件做出反应,且可以处理比其他数据处理系统大得多的数据量:直接处理事件流,并且只保留数据中有意义的子集。尤其是面对持续生成,本质上是无穷尽的数据集。
Java Stream 流
JDK 1.8 新增。将要处理的元素集合看作一种流,在管道的节点上进行处理。使代码更简洁易读。
集合接口有两个方法来生成流,数据类型将由 Collection 转化为 Stream 。
stream方法:为集合创建串行流。parallelStream方法:为集合创建并行流。
- Stream 的遍历方式和结果与 Iterator 无差别(便于转化),其优势在于其原型链的设计使得它可以对遍历处理后的数据进行再处理。
- parallelStream 提供了流的并行处理,底层使用 Fork/Join 框架,简单理解就是多线程异步任务的一种实现。处理过程中会有多个线程处理元素,具体由 JDK 负责管理。不保证有序性。
- 串行流和并行流之间可以通过
parallel和sequential方法相互转化。
Stream<Integer> stream = list.stream(); // 声明作为流处理
ParellerStream<Integer> pStream = stream.parallel(); // 转化为并行流
流操作
流处理的每个操作阶段都会封装到一个 Sink 接口里,处理数据后再将数据传递给下游的 Sink。
Stream 上的所有操作分为两类:中间操作和结束操作。Stream 是延迟执行的,只有调用到结束操作,才触发整个流水线的执行。如果未定义结束操作,那么流处理什么也不会做。
// 获取空字符串的数量
int count = strings.parallelStream() // 声明作为流处理
.filter(string -> string.isEmpty()) // 中间操作,过滤空元素
.count(); // 结束操作,计数Copy to clipboardErrorCopied
获取Stream流
/** --------------------Collection集合获取流------------------------------- */
Collection<String> list = new ArrayList<>();
Stream<String> s = list.stream();
/** --------------------Map集合获取流------------------------------- */
Map<String, Integer> maps = new HashMap<>();
// 键流
Stream<String> keyStream = maps.keySet().stream();
// 值流
Stream<Integer> valueStream = maps.values().stream();
// 键值对流(拿整体)
Stream<Map.Entry<String,Integer>> keyAndValueStream = maps.entrySet().stream();
/** ---------------------数组获取流------------------------------ */
String[] names = {"赵敏","小昭","灭绝","周芷若"};
Stream<String> nameStream = Arrays.stream(names);
Stream<String> nameStream2 = Stream.of(names);
中间操作
加工 map
map 方法用于映射每个元素到对应的结果,其实就是对结果进行转化。
List<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三丰", "赵敏", "小昭");
// 给集合元素的前面都加上一个:牛马的:
// 第一个参数原材料 -> 第二个参数是加工后的结果。
list.stream().map(s -> "牛马的:" + s).forEach(a -> System.out.println(a));
过滤 filter
filter 方法用于通过设置的条件过滤出元素。
list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);
筛选 limit/skip
limit 方法用于获取指定数量的流(前 n 个), skip 方法用于去除指定数量的流(前 n 个)。
list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);
排序 sorted
sorted 方法通过 Comparable 接口对流进行排序,也可以自定义。
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
合并 concat
// 合并流。
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
Stream<String> s2 = Stream.of("java1", "java2");
// public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
Stream<String> s3 = Stream.concat(s1 , s2);
去重 distinct
distinct 方法通过流元素的 hashCode 和 equals 方法去除重复元素。
s3.distinct().forEach(s -> System.out.println(s));
求和 reduce
List<Integer> list= Arrays.asList(new Integer[]{1,2,3,4,5,6,7,8,9});
Integer sum=list.stream().reduce((x,y)->x+y).get();
System.out.println(sum);
结束操作
最值 max
// 获取最高工资的员工,封装成优秀员工对象
List<Employee> one = new ArrayList<>();
Topperformer t = one.stream().max((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus()))
.map(e -> new Topperformer(e.getName(), e.getSalary() + e.getBonus())).get();
// get() 获取对象
// 2、统计平均工资,去掉最高工资和最低工资
one.stream().sorted((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(), e2.getSalary() + e2.getBonus()))
.skip(1).limit(one.size() - 2).forEach(e -> {
// 求出总和:剩余员工的工资总和
allMoney += (e.getSalary() + e.getBonus());
});
// allMoney 由于不同方法栈中变量不能共享,所以allMoney 需要定义成静态成员,静态成员是可以全局共享的
迭代 forEach
结束操作: forEach 迭代流中的每个数据,即对每个数据进行最后的处理(比如保存到数据库中或打印)。
// 需求:把所有的名称 都加工成一个学生对象。
list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
// list.stream().map(Student::new).forEach(System.out::println); // 构造器引用 方法引用
聚合 Collectors
结束操作:Collectors 类实现了归约操作,例如将流转换成集合和聚合元素,可用于返回列表或字符串。
// Stream 转化为 List
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
// Stream 转化为 String
String mergedString = strings.stream() .filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
// 注意注意注意:“流只能使用一次”
// Stream 转化为 List
Stream<String> Set= list.stream().filter(s -> s.startsWith("张"));
Set<String> zhangSet = s2.collect(Collectors.toSet());
// Stream 转化为 Array
Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
// Object[] arrs= s3.toArray(); // 转换为 Object类型数组
String[] arrs = s3.toArray(new IntFunction<String[]>() { // 转换为自定义类型的数组
@Override
public String[] apply(int value) {
return new String[value];
}
});
}
// Stream 转化为 Map
Map<String, Double> map = list.stream().collect(Collectors.toMap(e -> e.getName(), e -> e.getHeight())); //需要保证key唯一
统计 SummaryStatistics
结束操作:收集最终产生的统计结果,它们主要用于 int、double、long 等基本类型上。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());

浙公网安备 33010602011771号