在我们学习JDK8的过程中,有一个非常重要的知识点,那就是Stream流了,Stream流能取代频繁的for循环,提升了代码执行效率,而且内置了很多方法处理集合大大简化了处理集合的繁琐复杂步骤,但是方法比较多,学起来比较生硬;本文章以作者本人在工作中实际的业务场景来介绍stream流各个方法的使用,以下以业务场景为单元介绍点,结合各个stream流的方法简化以前繁杂的普通代码。
1、List<Map>里的某个属性值取出来拼成一串字符串
before :需要for循环一个一个把元素取出来用字符串拼接,拼接完后还需要去除最后一个拼接符,非常麻烦
for (int i = 0; i < mapList.size(); i++) {
ids += mapList.get(i).get("id") + ",";
}
if (ids != null && ids .length() > 0) {
ids = ids .substring(0, ids .length() - 1);
}
after :先用map取出属性值转成Stream<String> 然后用终结方法的joing()方法直接拼接
ids = mapList.stream()
.map(item->item.get("id").toString())
.collect(Collectors.joining(","));
(List<String> list 当然也可以用String.join(",", list)的方法直接拼接更快更简单,本篇主要以介绍Stream流的方法为主,故尽量使用Stream流的方法)
2、List<Map>需要取最大(小)的某个值
before :通过for循环把元素取出来和最大值maxScore比较,大于则取代新的最大值
Integer maxScore = 0;
for (Map item : mapList) {
if (item.getInt("score") > max ) {
max = item.getInt("score");
}
}
after :先用map取出属性值转成Stream<Integer> 然后用reduce归纳方法获取到需要的数据
Integer maxScore = mapList.stream()
.map(t -> t.getInt("score"))
.reduce(0, (x, y) -> x > y ? x : y);
这里介绍一下reduce方法的使用:
如果需要将所有数据归纳得到一个数据,可以使用reduce方法。方法签名:
//T identity : 默认值
// Binaryoperator<T> accumulator : 对数据进行处理的方式
reduce(T identity, Binaryoperator<T> accumulator ) ;
/**
reduce如何执行?
第一次,将默认值赋值给x,取出集合第一元素赋值给y 进行运算得出结果
第二次,将上一次返回的结果赋值x,取出集合第二元素赋值给y 进行运算得出结果
第三次,将上一次返回的结果赋值x,取出集合第三元素赋值给y 进行运算得出结果
...
*/
Integer reduce = Stream.of(4, 5, 6, 7).reduce(0, (x, y) -> {
System.out.println("x=>" + x + " y=>" + y);
return x + y;
});
System.out.println("reduce = " + reduce);
// 得到的结果是[4, 5, 6, 7]的总和
打印结果:
x=>0 y=>4
x=>4 y=>5
x=>9 y=>6
x=>15 y=>7
reduce = 22
取最小、平均、求和这种将集合的数据归纳成一个数据时均可用reduce方法
Integer sum = mapList.stream()
.map(t -> t.getInt("score"))
.reduce(0, Integer::sum);
3、JSONArray转成JSONObject的List形式使用Stream流
JSONArray orgList;
orgList.stream()
.map(o ->{
if ( o instanceof LinkedHashMap){
return JSONObject.parseObject(JSON.toJSONString(o));
}else{
return (JSONObject)o;
}
}).forEach(item ->{
// item已经是JSONObject,可以使用里面的方法了
...
});
4、两个List<Map>取交集,然后进行处理
假设有用户集合userList,和用户部门关联集合deptList,这里以用户集合userList作为主体,两集合之间有两种对应关系;一种是一对一,另一种是一对多(可理解成 一个用户只能有一个部门 / 一个用户可以有多个部门);两种情况处理方式类似,只是分组方式不同。
① 一对一
// Dept={"deptId":"","deptName":"",userId:""}
List<Dept> deptList;
// 以用户id作为key构建一个map value存的是对应的部门Dept
Map<String, Dept> userDeptMap= deptList
.stream()
.collect(Collectors.toMap(
Dept::getUserId,
Function.identity(),
(x, y) -> x
));
// User={"userId":"",userName:""}
List<User> userList;
userList.stream().forEach(user->{
if (userDeptMap.containsKey(id)){
Dept dept = userDeptMap.get(id);
}else{
// 改用户不存在对应的部门
}
});
通过将 集合deptList构建成Map<String,Dept>的形式,方便于主体集合userList取用,从而避免了循环中再次嵌套循环,优化了代码执行效率;
② 一对多
// Dept={"deptId":"","deptName":"",userId:""}
List<Dept> deptList;
// 以用户id作为key构建一个map value存的是对应的部门集合List<Dept>
Map<String, List<Dept>> userDeptMap= deptList
.stream()
.collect(Collectors.groupingBy(Dept::getUserId));
// User={"userId":"",userName:""}
List<User> userList;
userList.stream().forEach(user->{
if (userDeptMap.containsKey(id)){
List<Dept> deptList = userDeptMap.get(id);
}else{
// 改用户不存在对应的部门
}
});
和①大体类似,唯一区别是①的分组方法是Collectors.toMap() ②的分组方法是Collectors.groupingBy()
5、用流来进行简单分页
当不方便在SQL查询做好分页时,此时就必须要在代码层来进行分页,可以使用limit和skip的结合使用达到分页效果
long pageNo = listQueryModel.getPageNum();
long pageSize = listQueryModel.getPageSize();
list = list.stream().skip((pageNo - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
Stream流能大大简化代码的编写,使代码看起来简洁漂亮,唯一不足可能是可读性稍差点,这也只是对于不熟悉流的使用人而言,后续深度使用便不存在此问题,而且你会发现Stream流真的真的很好用!另外还有很多业务场景用到Stream流,想起来再慢慢更新吧!
-------------------------------- to be continue --------------------------------