Java 8 基于外部阈值列表过滤List元素的实现优化
学生成绩过滤逻辑的Stream实现优化方案
你当前基于rollNum分组后逐组校验达标状态的实现逻辑完全符合需求,核心可优化点是减少中间集合的内存开销、精简冗余操作,以下是可直接落地的优化方案:
原实现的可优化点
常规分组实现的代码一般如下:
// 原有分组实现参考 Map<String, List<StudentMark>> studentGroup = studentMarks.stream() .collect(Collectors.groupingBy(StudentMark::getRollNum)); List<StudentMark> result = new ArrayList<>(); studentGroup.forEach((rollNum, markList) -> { boolean pass = markList.stream() .anyMatch(m -> m.getScore() > Metadata.SUBJECT_PASS_THRESHOLD.get(m.getSubject())); if (pass) result.addAll(markList); });
这个写法的问题是groupingBy操作会把全量成绩记录按学号重新存储一份到Map中,相当于数据集在内存中多了一份完整拷贝,数据量越大额外内存开销越高,同时分组过程的Entry创建、列表初始化也会带来不必要的性能损耗。
优化方案1:两次遍历实现(推荐,可读性+性能平衡)
不需要做全量分组,第一次遍历仅收集存在至少一科达标学生的学号到Set中,第二次遍历原列表直接过滤出学号在达标Set中的记录即可:
// 第一步:收集所有达标的学生学号 Set<String> qualifiedRollNums = studentMarks.stream() .filter(m -> m.getScore() > Metadata.SUBJECT_PASS_THRESHOLD.get(m.getSubject())) .map(StudentMark::getRollNum) .collect(Collectors.toSet()); // 第二步:过滤保留达标学生的全部成绩 List<StudentMark> result = studentMarks.stream() .filter(m -> qualifiedRollNums.contains(m.getRollNum)) .collect(Collectors.toList());
- 时间复杂度和原分组实现一致,均为O(n),但中间集合仅存储达标学生的学号,内存占用远低于全量分组的Map
- 完全适配业务变化:不管
Metadata中增减多少科目、studentMarks有多少学生、学生成绩顺序如何,逻辑都不需要调整 - 代码行数更少,逻辑直观,后续维护成本低
优化方案2:大数据量场景的并行提速
如果studentMarks数据量在10万条以上,且最终结果不需要严格保留原列表的成绩顺序,可以改用并行流配合不可变集合收集,进一步提升处理速度:
Set<String> qualifiedRollNums = studentMarks.parallelStream() .filter(m -> m.getScore() > Metadata.SUBJECT_PASS_THRESHOLD.get(m.getSubject())) .map(StudentMark::getRollNum) .collect(Collectors.toUnmodifiableSet()); List<StudentMark> result = studentMarks.parallelStream() .filter(m -> qualifiedRollNums.contains(m.getRollNum)) .collect(Collectors.toUnmodifiableList());
注意:如果业务要求输出结果必须和原列表的成绩顺序保持一致,不要使用并行流,会打乱原有记录顺序。
优化方案3:单次遍历实现(仅超大数据量场景可选)
如果业务要求只能遍历一次原列表,可以通过一个Map边遍历边标记学生达标状态、暂存未达标学生的已遍历记录:当遍历到某学生第一条达标的成绩时,把之前暂存的该学生所有记录加入结果集,后续该学生的成绩直接写入结果集即可。但这个写法代码复杂度高、可读性差,除非是千万级以上的超大数据量场景,否则不推荐使用。
内容的提问来源于stack exchange,提问作者curious_brain
相关产品推荐
相关产品推荐

