复用HashMap并清空后,集合元素受影响的技术问题咨询
问题原因分析与解决方案
这是个非常典型的Java引用类型认知问题,咱先拆解清楚为啥会出现这个现象:
核心本质:Java中引用类型的存储逻辑
Java里像HashMap这种对象是存在堆内存里的,而你定义的变量aMap其实只是一个指向堆中这个Map对象的引用地址,并不是对象本身。当你执行list.add(aMap)时,List里保存的不是这个Map的副本,而是和aMap一模一样的引用地址——也就是说,List里的两个元素,从头到尾都指向同一个HashMap对象!
你的代码执行流程拆解
- 初始化
aMap并放入{"foo":"bar"},把引用添加到List,此时List第一个元素指向这个有值的Map; - 调用
aMap.clear():这是直接修改堆里的那个Map对象,把它清空了——因为List第一个元素和aMap指向同一个对象,所以List里的第一个元素也跟着变成空Map了; - 再给
aMap放入{"big":"bang"},还是修改同一个堆对象,然后把同一个引用再次添加到List,现在List的两个元素都指向这个新的有值Map; - 最后打印List,自然会输出两个相同的
{"big":"bang"}。
解决方法
这里给你两种常用的解决思路:
方法1:每次添加后创建新的Map实例,放弃复用原变量的引用
直接每次需要新Map时,创建一个新的HashMap对象,这样List里的元素会指向不同的堆对象,互相不会影响:
List<Map> list = new ArrayList<>(); Map<String,String> aMap = new HashMap<>(); aMap.put("foo","bar"); list.add(aMap); // 创建新的Map实例,原引用指向新对象 aMap = new HashMap<>(); aMap.put("big", "bang"); list.add(aMap); System.out.println(list.toString());
方法2:复用变量,但往List里添加Map的副本
如果一定要复用aMap变量,那每次添加到List时,添加原Map的拷贝副本,而不是原引用。可以用HashMap的拷贝构造函数实现:
List<Map> list = new ArrayList<>(); Map<String,String> aMap = new HashMap<>(); aMap.put("foo","bar"); // 添加原Map的副本到List,而非原引用 list.add(new HashMap<>(aMap)); aMap.clear(); aMap.put("big", "bang"); // 再次添加副本 list.add(new HashMap<>(aMap)); System.out.println(list.toString());
内容的提问来源于stack exchange,提问作者Navalona Ramanantoanina
相关产品推荐
相关产品推荐

