前言
嗨,大家好,我是希留,一个被迫致力于全栈开发的老菜鸟。
近日项目里面有个新需求,一个列表原先是通过sql语句groupBy分组得到的集合。但现在由于业务需求变动,结果集的某个状态需要动态生成,再根据这个状态进行求和,所以就不能使用sql直接查询出结果,需要在程序里面处理。
例如下面的例子,这种集合分组操作首先想到的就是使用Stream的groupBy来处理,Stream的基本用法应该都知道吧,不会的可以移步【Java8新特性Stream的常见用法】了解一下。
由于是多字段分组,且还有根据不同条件求和,比较少用到。所以还是值得记录一下,大家对于这种业务如果还有更优雅的实现方式,也请评论区指点一下,感谢😘
SQL语句的原型:
大概的业务就是:根据 learn_state 状态来判断相加 course_credit字段的和 得到 gainCredit,在判断 gainCredit 是否大于等于moduleLowestCredit,得到 state
SELECT
xrm.module_name moduleName,
xrm.module_lowest_credit moduleLowestCredit,
xrm.module_center_lowest_credit moduleCenterLowestCredit,
sum( CASE WHEN xsc.learn_state = '4' THEN xcm.course_credit ELSE 0 END ) gainCredit,
CASE WHEN sum( CASE WHEN xsc.learn_state = '4' THEN xcm.course_credit ELSE 0 END )>= xrm.module_lowest_credit
THEN '已满足毕业条件' ELSE '未满足毕业条件' END state
FROM
table
WHERE
GROUP BY
xrm.module_name,
xrm.module_lowest_credit,
xrm.module_center_lowest_credit
复制代码
通过 Stream 实现
通过程序实现就需要把原先sql分组的数据都查询出来,不进行分组。如下例子:
得到所有待分组的数据,通过 stream 进行分组 。
@Test
public void testStreamGroupBySum(){
// 要分组的数据
List<Map<String, Object>> courseDetail = new ArrayList<>();
Map<String, Object> testMap1 = new HashMap<>();
testMap1.put("moduleName", "公共基础课");
testMap1.put("moduleLowestCredit", "7");
testMap1.put("moduleCenterLowestCredit", "7");
testMap1.put("gainCredit", "3");
testMap1.put("learnState", "1");
courseDetail.add(testMap1);
Map<String, Object> testMap2 = new HashMap<>();
testMap2.put("moduleName", "公共基础课");
testMap2.put("moduleLowestCredit", "7");
testMap2.put("moduleCenterLowestCredit", "7");
testMap2.put("gainCredit", "3");
testMap2.put("learnState", "4");
courseDetail.add(testMap2);
Map<String, Object> testMap3 = new HashMap<>();
testMap3.put("moduleName", "公共英语课");
testMap3.put("moduleLowestCredit", "4");
testMap3.put("moduleCenterLowestCredit", "3");
testMap3.put("gainCredit", "4");
testMap3.put("learnState", "4");
courseDetail.add(testMap3);
Map<String, Object> testMap4 = new HashMap<>();
testMap4.put("moduleName", "公共英语课");
testMap4.put("moduleLowestCredit", "4");
testMap4.put("moduleCenterLowestCredit", "3");
testMap4.put("gainCredit", "2");
testMap4.put("learnState", "4");
courseDetail.add(testMap4);
// 多个字段分组统计处理
Map<String, List<Map<String, Object>>> collectMap = courseDetail.stream().collect(Collectors.groupingBy(d ->
d.get("moduleName") + "_" +
d.get("moduleLowestCredit")+ "_" +
d.get("moduleCenterLowestCredit"),Collectors.toList()));
// 得到分组后的集合
List<Map<String, Object>> newCourseDetail = collectMap.keySet().stream().map(key -> {
Map<String, Object> pointMap = new HashMap<>();
String[] temp = key.split("_");
pointMap.put("moduleName", temp[0]);
pointMap.put("moduleLowestCredit", temp[1]);
pointMap.put("moduleCenterLowestCredit", temp[2]);
List<Map<String, Object>> collectList = collectMap.get(key);
if (CollectionUtil.isNotEmpty(collectList)) {
int tempCredit = 0;
for (Map<String, Object> map : collectList) {
// 当条件是4时,相加分数
if ("4".equals(String.valueOf(map.get("learnState"))) && StrUtil.isNotBlank(String.valueOf(map.get("gainCredit")))) {
tempCredit += Integer.parseInt(String.valueOf(map.get("gainCredit")));
}
}
pointMap.put("gainCredit", tempCredit);
pointMap.put("state", tempCredit >= Integer.parseInt(temp[1]) ? "已满足毕业条件" : "未满足毕业条件");
}
return pointMap;
}).collect(Collectors.toList());
System.out.print(courseDetail);
System.out.print(newCourseDetail);
}
复制代码
输出结果
从输出的结果可以看出,已经得到了和原先sql一致的的结果集了。
courseDetail: [{
moduleLowestCredit = 7,
gainCredit = 3,
moduleName = 公共基础课,
learnState = 1,
moduleCenterLowestCredit = 7
}, {
moduleLowestCredit = 7,
gainCredit = 3,
moduleName = 公共基础课,
learnState = 4,
moduleCenterLowestCredit = 7
}, {
moduleLowestCredit = 4,
gainCredit = 4,
moduleName = 公共英语课,
learnState = 4,
moduleCenterLowestCredit = 3
}, {
moduleLowestCredit = 4,
gainCredit = 2,
moduleName = 公共英语课,
learnState = 4,
moduleCenterLowestCredit = 3
}]
newCourseDetail: [{
moduleLowestCredit = 4,
gainCredit = 6,
moduleName = 公共英语课,
state = 已满足毕业条件,
moduleCenterLowestCredit = 3
}, {
moduleLowestCredit = 7,
gainCredit = 3,
moduleName = 公共基础课,
state = 未满足毕业条件,
moduleCenterLowestCredit = 7
}]
复制代码
总结
好了,以上就是本文的全部内容了,感谢大家的阅读。
本文通过一次实际项目案例,记录了使用 stream的多字段groupBy 来实现类似 mysql 中 sum、case when groupBy分组等语法。
如果有什么疑问或者建议,欢迎评论区留下你的独到见解~