hashCode()和equals(),为什么使用Map要重写这两个,为什么重写了hashCode,equals也需要重写
文章目录
- hashCode()
- equals()
- HashMap的存储规则
- 未重写hashCode()和equals()的情况
- 仅重写equals()的情况
- 仅重写hashCode()的情况
- 同时重写hashCode()和equals()的情况
- 关键结论
- 总结
hashCode()
默认返回对象的内存地址转换的int值。
equals()
默认比较两个对象的内存地址是否相同。
HashMap的存储规则
- 当对象需要存入map时,使用hashCode() 获得对象的hash码,和数组的长度取余得到一个存储位置
- 如果位置为空,直接存入
- 如果位置不为空,说明发生了hash冲突,使用equals进行判断,是否是相同的
- equals判断相同,则说明存储元素重复,map覆盖,set不存入
- equals判断不相同,说明元素应该存入,散列到其他位置
未重写hashCode()和equals()的情况
- hashCode()
- 默认返回对象的内存地址转换的int值
- 相同对象(内存地址相同)返回相同的hashCode
- equals()
- 默认比较两个对象的 内存地址 是否相同
- 存入Map的行为
- 如果两个属性值相同的对象存入Map
- 第一次存入时,计算hashCode并放入对应桶
- 第二次存入时,由于内存地址不同:
- hashCode() 可能不同(取决于 JVM 实现),可能发生哈希冲突
- 即使 hashCode 相同, equals()比较内存地址仍返回false
- 最终两个对象都会存入Map(违反业务逻辑)
- 如果两个属性值相同的对象存入Map
仅重写equals()的情况
- hashCode()
- 未重写,仍返回内存地址的hashCode
- equals()
- 重写后比较对象的属性值是否相同
- 存入Map的行为
- 如果两个属性值相同的对象存入Map
- 第一次存入时,计算 hashCode 并放入对应桶
- 第二次存入时:
- hashCode() 可能不同(内存地址不同),可能发生哈希冲突
- 即使hashCode相同,equals()比较属性值返回 true ,但Map仍认为它们是不同对象(因为内存地址不同)
- 最终两个对象都会存入Map(违反业务逻辑)
- 如果两个属性值相同的对象存入Map
仅重写hashCode()的情况
- hashCode()
- 根据对象的属性值计算hashCode
- equals()
- 默认比较两个对象的内存地址是否相同
- 存入Map的行为
- 如果两个属性值相同的对象存入Map
- 第一次存入时,计算hashCode并放入对应桶
- 第二次存入时:
- hashCode()相同(属性值相同),进入同一桶
- 即使hashCode相同,但是equals()比较属性值返回false(因为内存地址不同)
- 最终两个对象都会存入Map (违反业务逻辑)
- 如果两个属性值相同的对象存入Map
同时重写hashCode()和equals()的情况
- hashCode()
- 根据对象的属性值计算hashCode
- equals()
- 比较对象的属性值是否相同
- 存入Map的行为
- 如果两个属性值相同的对象存入Map
- 第一次存入时计算hashCode并放入对应桶
- 第二次存入时:
- hashCode()相同(属性值相同),进入同一桶
- equals()比较属性值返回true,Map认为是同一个对象
- 最终只存入一次(符合业务逻辑)
关键结论
- hashCode()和equals()必须同时重写
- 如果只重写 equals() , Map 可能存储重复对象(因为 hashCode() 仍比较内存地址)。
- 如果只重写 hashCode() , equals() 仍比较内存地址,可能导致逻辑错误(如 HashSet 判断重复失效)。
- 两套逻辑的本质
- 内存地址比较 (未重写):适用于判断对象是否是同一个实例。
- 属性值比较 (重写后):适用于判断业务上是否是“相同”的对象(如两个 User 对象 id 和 name 相同即视为相同)。
- Map的存储规则
- 先比较 hashCode() ,再比较 equals() 。
- 只有 hashCode()相同且equals()返回true时,才认为是同一个对象。
总结
本质上就是使用了两套逻辑进行判断,一套使用的是地址,一套使用的是属性值,所以如果只重写一部分,就会发生问题,存储重复的值。
另外使用hashMap存储对象,必须要重写这两个方法,原因参考未重写hashCode()和equals()的情况。