Java HashMap存储学生成绩问题:复用List致数据覆盖的原因排查
问题原因与解决方案
你遇到的核心问题是Java中对象引用的传递机制:HashMap存储的不是ArrayList的副本,而是指向ArrayList对象的内存引用。你复用同一个marks对象,每次调用clear()和添加新元素时,修改的都是同一个内存地址里的列表实例,导致map中所有关联的学生都会指向这个被反复修改的列表,最终所有学生的成绩都会变成最后一次添加的内容。
代码运行过程拆解
- 第一次
put后,map里的"Rakesh"指向marks对象(内容为[12,10,15]) - 调用
marks.clear()后,该对象内容被清空,map中"Rakesh"对应的列表也同步为空 - 第二次添加元素后,
marks对象内容变为[9,8,15],此时map里的"Rakesh"和"Ashwin"都指向这个列表,打印时两个学生的成绩完全一致 - 第三次重复操作后,三个学生的成绩都会变成最后一次添加的
[7,11,8]
解决方案
方案1:每次创建新的ArrayList对象
不再复用同一个marks实例,处理每个学生时新建独立的ArrayList,确保每个学生对应唯一的列表对象:
HashMap<String, ArrayList<Integer>> map = new HashMap<>(); // 1st student detail ArrayList<Integer> marks = new ArrayList<>(); marks.add(12); marks.add(10); marks.add(15); map.put("Rakesh", marks); System.out.println(map); // 2nd student detail marks = new ArrayList<>(); // 新建独立对象 marks.add(9); marks.add(8); marks.add(15); map.put("Ashwin", marks); System.out.println(map); // 3rd student detail marks = new ArrayList<>(); // 新建独立对象 marks.add(7); marks.add(11); marks.add(8); map.put("Rahul", marks); System.out.println(map);
方案2:存入map时创建ArrayList副本
如果一定要复用原marks对象,每次put到map时传入当前列表的副本,这样map存储的是独立的副本,后续修改原marks不会影响已存入的数据:
HashMap<String, ArrayList<Integer>> map = new HashMap<>(); ArrayList<Integer> marks = new ArrayList<>(); // 1st student detail marks.add(12); marks.add(10); marks.add(15); map.put("Rakesh", new ArrayList<>(marks)); // 存入副本 System.out.println(map); marks.clear(); // 2nd student detail marks.add(9); marks.add(8); marks.add(15); map.put("Ashwin", new ArrayList<>(marks)); // 存入副本 System.out.println(map); marks.clear(); // 3rd student detail marks.add(7); marks.add(11); marks.add(8); map.put("Rahul", new ArrayList<>(marks)); // 存入副本 System.out.println(map);
扩展:实现成绩排名前三的需求
解决存储问题后,可通过计算总成绩并排序来获取排名前三的学生:
// 计算总成绩并按倒序排序,取前3名 List<Map.Entry<String, ArrayList<Integer>>> sortedStudents = map.entrySet() .stream() .sorted((e1, e2) -> { int sum1 = e1.getValue().stream().mapToInt(Integer::intValue).sum(); int sum2 = e2.getValue().stream().mapToInt(Integer::intValue).sum(); return Integer.compare(sum2, sum1); // 倒序排列 }) .limit(3) .toList(); // 输出结果 System.out.println("成绩排名前三的学生:"); sortedStudents.forEach(entry -> { int total = entry.getValue().stream().mapToInt(Integer::intValue).sum(); System.out.println(entry.getKey() + " - 总成绩:" + total); });
内容的提问来源于stack exchange,提问作者Rakesh
相关产品推荐
相关产品推荐

