JDK8中的Stream流
前言
jdk8版本,多出了很多很重要的特性。
其中最重要的Lambda和Stream能够让我们写出高效率、干净、简洁的代码。
本文目的是让大家更加轻松地学习并理解Stream流,能够应用到实际的项目中。
认识Stream流
- 提取对象中的某个值生成一个新的List
Bash
// 生成数据
Map<String, String> arg1 = new HashMap<>();
arg1.put("name", "dog");
arg1.put("age", "3");
Map<String, String> arg2 = new HashMap<>();
arg2.put("name", "cat");
arg2.put("age", "8");
// 转换数据
List<Integer> ageList = Stream.of(arg1, arg2)
.map(map -> Integer.valueOf(map.get("age")))
.collect(Collectors.toList());
// ageList 为 3,8
- 统计一个对象某个值的总和
Bash
// 生成数据
Map<String, String> arg1 = new HashMap<>();
arg1.put("name", "dog");
arg1.put("age", "3");
Map<String, String> arg2 = new HashMap<>();
arg2.put("name", "cat");
arg2.put("age", "8");
// Stream流统计年龄合计
Optional<Integer> ageSum = Stream.of(arg1, arg2)
.map(map -> Integer.valueOf(map.get("age")))// map 数据转换
.reduce(Integer::sum);// 归约操作,Integer::sum 等价 (a,b)->a+b; 其中a为上次计算的结果,b为新的值,然后返回新的值,向后一次类推,a的默认值0。
// ageSum 的值为11
上面两个例子介绍了Stream流最重要的两个使用场景
数据的转换和计算
其实Stream流并不复杂,大多数人不会用只是不知道其使用场景。
每个Stream流只能使用一次,一旦终结,如果继续调用的话,会抛出异常,这点一定要注意
Stream常用方法
Stream流中每个方法(除了终结方法)都会返回一个流,当调用终结方法时,整个流才会启动
可以理解为一个工作流水线,最后一步是出产品,在这之前都是对产品加工
下面介绍下加工方法(流水线上的每一步)和终结方法(最终产品的输出)最常用的一些
加工方法(数据加工的方法)
- map(Function<? super T, ? extends R> mapper)对数据进行转换,相当于一次foreach,然后把每次返回的值作为新流的数据
- filter(Predicate<? super T> predicate)对数据做过滤,不符合条件的不会加入到后的流中
- limit(long maxSize)限制流的最大数量,超出部分截断
- skip(long n)跳过n条数据,和limit配合使用,类似MySql中的limit skip,limit
终结方法(对加工完的数据包装输出)
- forEach(Consumer<? super T> action)遍历,迭代
- collect(Collector<? super T, A, R> collector)转换为集合类型或分组处理数据,如:.collect(Collectors.toList())转换为List类型,还有toSet和toMap等
- reduce(BinaryOperator<T> accumulator)归约操作,每一次调用的结果作为下一次调用的参数,遍历一遍后返回结果
PS:能够掌握上面的方法就能够基本适应大多数场景的要求了
例子
统计每天所有页面的UV(访问的用户数),PV(只要有访问就算一次)
日志数据结构AccessLog
Bash
// 用户唯一标识
private Long uid;
// 页面唯一标识
private String pageID;
// 访问日期
private LocalDate time;
统计结果数据结构Result
Bash
// UV
private Long uv;
// PV
private Long pv;
// 页面唯一标识
private String pageID;
// 统计日期
private LocalDate time;
统计过程代码
Bash
List<AccessLog> logList = new ArrayList<>();
// ... 数据赋值给logList,省略获取数据的过程
List<Result> resultList = new ArrayList<>();
// 首先将数据处理成按时间分组的数据
Map<LocalDate, List<AccessLog>> groupByTime = logList.stream().collect(Collectors.groupingBy(AccessLog::getTime));
// 遍历每一天的数据
groupByTime.forEach((localDate, timeList) -> {
// 再按照页面ID分组,这样就得到了每天每个页面的所有访问日志,接下来就是统计了
Map<String, List<AccessLog>> groupByPage = timeList.stream().collect(Collectors.groupingBy(AccessLog::getPageID));
// 在遍历页面分组信息
groupByPage.forEach((pageId, pageTimeList) -> {
Result result = new Result();
result.setPageID(pageId);
result.setTime(localDate);
result.setUv(pageTimeList.stream().map(AccessLog::getUid).distinct().count());
result.setUv((long) pageTimeList.size());
resultList.add(result);
});
});
// 输出统计结果
resultList.forEach(System.out::println);
小伙伴们可以想一下,如果不用Stream实现这个需求,要写多少代码才可以完成ヾ(≧▽≦*)o
最后
常用的方法介绍到这里,以后也会继续更新,最重要的是能在平时开发中使用的上,真正提升开发体验的知识;当然也会分享一下自己平时常用的工具和coding技巧给大家,也欢迎评论指出问题和或者提出建议,我都会尽量回答和听取大家建议。
PS:第一次写技术类分享文章,希望大家理性看待,不要暴躁....