HashMap使用可变对象作为键的迭代行为异常原因咨询
问题根源:混淆了「引用变量」和「实际存储的对象」
你遇到的困惑核心在于没搞清楚Java中引用变量和它指向的实际对象之间的区别,以及HashMap存储键的本质。咱们一步步拆解你的操作和现象:
1. 初始存储阶段:HashMap存的是对象,不是变量
当你执行:
Employee e1 = new Employee("sachin",123); map.put(e1, "lo");
HashMap里存储的是new Employee("sachin",123)这个实际对象,而不是e1这个变量。e1只是一个指向这个对象的"指针"而已。同理,e2和e3指向的对象也被存入了map,但因为e2和e3的equals()返回true、hashCode()相同,所以put(e3)会覆盖e2对应的value,此时map里实际有两个键对象:
- 对象A:
name=sachin, id=123(最初e1指向的对象) - 对象B:
name=sachin, id=456(e2和e3指向的对象)
2. 改变e1的指向:不会影响HashMap里的对象
当你执行:
e1= new Employee("hrithik",123);
这只是让e1这个指针重新指向了一个新的对象C,但原来的对象A依然好好待在HashMap里,没有任何变化。所以此时map.get(e1)返回null,是因为对象C不在map里,这符合预期。
接着你执行:
e1=e2;
这只是让e1现在指向对象B(和e2指向同一个对象),但HashMap里的对象A还是存在的——你只是改变了e1的指向,并没有从map里移除对象A,也没有修改对象A的内容。
3. 迭代时看到旧内容的原因
所以当你迭代map的键时,看到name=sachin, id=123的对象,就是最初的对象A,它从来没被修改或移除过。e1只是不再指向它了,但它依然是HashMap的键之一,自然会被迭代出来。
4. 修改对象B的empId后的现象
当你执行:
e2.setEmpId(300);
这是直接修改了对象B的属性(因为e2指向对象B,而对象B是HashMap里的键)。此时对象B的equals()逻辑会因为empId变化而改变,但你的hashCode()计算只依赖name的属性,所以hashCode不变。这会导致:
- HashMap无法通过
get()方法正确找到对象B(因为equals条件变了,但它还在原来的桶里) - 迭代时依然能看到对象B,因为迭代是遍历所有桶,不管键的hashCode和equals是否匹配当前状态
总结关键知识点
- HashMap存储的是对象本身的引用,不是你用来指向对象的变量。变量的赋值只是改变指针指向,不会影响HashMap里已存储的键。
- 如果要替换HashMap的键,你需要先调用
map.remove(oldKey)移除旧键,再调用map.put(newKey, value)添加新键,而不是单纯改变变量的指向。 - 作为HashMap键的对象,尽量不要修改影响
hashCode()或equals()的属性——如果必须修改,修改后要先移除旧键再重新存入,否则会导致HashMap的键"失效"(无法通过get找到,但仍存在于map中)。
内容的提问来源于stack exchange,提问作者user1600902
相关产品推荐
相关产品推荐

