Java8如何优化Stream分组求和后的Map排序实现
问题解答
完全可以在单条Stream流水线中完成全量逻辑,不需要拆分两次流操作,你的现有实现逻辑正确,但可以通过JDK提供的收集器组合实现更简洁的写法,减少中间变量和额外的流遍历开销。
优化实现方案
核心思路是用Collectors.collectingAndThen把「分组求和」和「排序后收集为有序Map」两个步骤串起来,作为stream的最终收集操作,全程不需要拆分对原始列表的流处理:
List<Progress> progresses = xxxx; return Optional.ofNullable(progresses) .orElse(Collections.emptyList()) .stream() .collect(Collectors.collectingAndThen( // 按studentId分组统计points总和 Collectors.groupingBy(Progress::getStudentId, Collectors.summingInt(Progress::getPoints)), // 对分组结果按积分降序排序,收集到LinkedHashMap保证排序顺序不丢失 sumMap -> sumMap.entrySet().stream() .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (oldVal, newVal) -> oldVal, // key唯一不会触发合并,仅为满足方法参数要求 LinkedHashMap::new )) ));
实现说明
- 不需要单独写空列表判断:用
Optional.ofNullable处理可能为null的入参,空列表直接走流收集逻辑,最终返回空的有序Map,和原有返回逻辑一致。 - 关于“单流完成”的边界:如果你要求的是对原始
progresses列表只做一次遍历,上述写法完全满足——原始列表只会被遍历一次完成分组聚合,后续排序操作的遍历对象是聚合后的学生维度Entry集合,数据量远小于原始列表;如果要求完全不能出现第二次Stream调用,从逻辑上不可能实现:排序依据是每个学生的总积分,必须等分组求和完成才能拿到排序所需的数值,所有实现都逃不开“先聚合、后排序”的步骤。 - 必须用
LinkedHashMap存储最终结果:普通HashMap是无序的,哪怕排好序插入,读取的时候也会打乱顺序,你之前代码里注释掉的LinkedHashMap::new就是用来保序的关键参数。 - 原有写法逻辑没有问题,只是拆分了两次流操作,性能差异极小,上述写法只是在代码结构上更连贯,减少了中间临时变量的定义。
内容的提问来源于stack exchange,提问作者Rajeev
相关产品推荐
相关产品推荐

