Java 1.7下如何高效生成多学生订阅的学科-学生ID映射表
嘿,这个问题我之前处理过类似的大数据量场景,Java 1.7下确实要尽量避免先全量构建再过滤的方式——太浪费内存和CPU了。给你两个高效的一次遍历方案,都是边处理边筛选,能大幅降低资源消耗:
方案一:实时维护Map,事后轻量过滤
这个方案在遍历过程中就先把所有学科的学生列表存起来,但只在最后做一次极小的过滤(移除单元素的条目),相比先存再删的原始思路,内存占用已经优化很多:
Map<String, List<String>> subjectToStudents = new HashMap<String, List<String>>(); for (Candidate candidate : candidates) { String subjId = candidate.getSubjectId(); String stuId = candidate.getStudentId(); List<String> stuList = subjectToStudents.get(subjId); if (stuList == null) { // 首次遇到该学科,初始化容量为2的列表(避免后续扩容) stuList = new ArrayList<String>(2); stuList.add(stuId); subjectToStudents.put(subjId, stuList); } else { stuList.add(stuId); } } // 最后移除仅单个学生的学科,用Iterator避免ConcurrentModificationException Iterator<Map.Entry<String, List<String>>> iter = subjectToStudents.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, List<String>> entry = iter.next(); if (entry.getValue().size() < 2) { iter.remove(); } }
为什么高效?
- 遍历过程中没有额外的判断逻辑,只做最基础的添加操作,速度快
- 初始化列表时设容量为2,减少小列表的扩容开销
- 最后过滤的是少数不符合条件的条目,相比全量遍历过滤,耗时可以忽略
方案二:仅保留符合条件的学科,完全避免事后过滤
如果数据量特别大,连存储单元素列表都觉得浪费内存,可以用两个辅助Map来跟踪首次出现的学生和计数,只把满足“多个学生”条件的学科加入结果Map:
Map<String, String> firstStudentOfSubject = new HashMap<String, String>(); Map<String, Integer> subjectCount = new HashMap<String, Integer>(); Map<String, List<String>> result = new HashMap<String, List<String>>(); for (Candidate candidate : candidates) { String subjId = candidate.getSubjectId(); String stuId = candidate.getStudentId(); Integer count = subjectCount.get(subjId); if (count == null) { // 第一次遇到该学科,记录计数和首个学生 subjectCount.put(subjId, 1); firstStudentOfSubject.put(subjId, stuId); } else if (count == 1) { // 第二次遇到,说明符合条件,创建列表并加入首个和当前学生 List<String> stuList = new ArrayList<String>(); stuList.add(firstStudentOfSubject.get(subjId)); stuList.add(stuId); result.put(subjId, stuList); subjectCount.put(subjId, 2); // 标记为已加入结果 } else { // 已经在结果里的学科,直接追加学生ID result.get(subjId).add(stuId); } }
为什么高效?
- 结果Map从始至终只存储符合条件的学科,完全不用后续过滤
- 辅助Map存储的是轻量的String键值对,比ArrayList占用内存少得多
- 遍历过程中的判断逻辑非常简单,对性能影响极小
额外优化建议
- 如果能预估符合条件的学科数量,给
resultMap设置初始容量(比如new HashMap<>(estimatedSize)),可以避免扩容带来的性能损耗 - 如果需要对学生ID去重(同一个学生多次订阅同一学科只保留一次),可以把
List换成HashSet,最后再转成List(如果业务需要List格式的话) - 若在多线程环境下处理,把
HashMap换成ConcurrentHashMap即可,Java 1.7的ConcurrentHashMap性能表现很不错
内容的提问来源于stack exchange,提问作者IUnknown
相关产品推荐
相关产品推荐

