必须同时重写equals和hashcode方法,否则会导致hashmap、hashset等哈希容器查找不到、存不进、去重失效;因哈希容器先用hashcode定位桶,再用equals精确比较,若equals为true但hashcode不同,对象会被散列到不同桶中。

因为 Java 集合(比如 HashMap、HashSet)依赖 hashCode 快速定位对象位置,再用 equals 精确判断是否重复;如果只重写 equals 而不重写 hashCode,逻辑上相等的对象可能被散列到不同桶中,导致查不到、存不进、去重失效。
哈希容器的工作机制决定二者必须一致
当对象作为 key 存入 HashMap 或加入 HashSet 时:
- JVM 先调用
hashCode(),算出它该放进哪个“桶”(数组索引) - 只在那个桶里,才逐个调用
equals()判断是否真正相等 - 如果两个对象
a.equals(b) == true,但a.hashCode() != b.hashCode(),它们就会被分到不同桶中——后续map.get(b)或set.contains(b)永远找不到a
违反 Java 规范契约会引发静默故障
Java 明确要求:只要 equals 返回 true,hashCode 就必须返回相同值。这不是建议,而是强制契约。违反后不会报错,但会出现:
-
HashSet允许存入两个equals为true的对象 → 本该去重却重复了 -
HashMap中用新构造的等价 key 调用get()返回null→ 数据明明存在却取不到 -
ConcurrentHashMap或LinkedHashSet同样行为异常,底层仍依赖哈希定位
重写时的关键实操原则
确保二者逻辑同步,避免手误或遗漏:
- 参与
equals比较的字段,必须全部用于hashCode计算(例如只比id,就只用id;若还加了name和email,哈希也得包含这三者) - 推荐用
Objects.hash(f1, f2, f3),自动处理null和类型转换,比手写更安全 - 避免使用可变字段(如后期会修改的
status)参与哈希计算,否则对象入集合后再改字段,哈希值变化 → 永远取不出来 - 继承场景下,子类若扩展了
equals判定字段,不仅要重写自己的hashCode,还要确认父类已正确实现
不重写的典型反例
假设 Person 类只重写了 equals(按 name 和 age 判断),但没动 hashCode:
-
p1 = new Person("Alice", 25),p2 = new Person("Alice", 25) -
p1.equals(p2)返回true,但p1.hashCode() == p2.hashCode()很可能是false(因用的是Object默认实现) -
Set<person> set = new HashSet(); set.add(p1); set.contains(p2)</person>返回false—— 本该是true
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











