必须重写hashCode(),因为Java规范强制要求equals()为true时hashCode()必须相等,否则HashMap、HashSet等集合会失效;需用所有参与equals()比较的字段共同计算哈希,推荐使用Objects.hash()。

如果两个对象 equals() 返回 true,它们的 hashCode() 必须相等;反之不成立。 这不是建议,是 Java 规范强制要求——违反它,HashMap、HashSet 等集合会直接“失灵”:对象存进去找不到、重复添加、remove() 失败,且毫无报错提示。
为什么重写 equals() 时必须重写 hashCode()
哈希集合(如 HashSet)内部靠 hashCode() 定位“桶”,再用 equals() 做精确比对。如果只重写 equals() 而不重写 hashCode(),两个逻辑相等的对象可能被散列到不同桶里,contains() 或 remove() 就永远找不到对方。
- 典型现象:
set.add(new Person("Alice", 25));成功,但set.contains(new Person("Alice", 25))返回false - 根本原因:两个
Person实例equals()为true,但默认hashCode()返回的是不同内存地址的哈希值 - 影响范围:所有基于哈希的集合和容器(
HashMap的 key、ConcurrentHashMap、LinkedHashSet等)
hashCode() 相等 ≠ equals() 相等
哈希冲突天然存在——不同对象算出相同哈希值完全正常,这不违规,也不需避免。关键在于:只要 equals() 为 true,哈希值就绝不能不同。
- 反例错误写法:
return name.hashCode();(忽略age字段),导致new Person("Alice", 25)和new Person("Alice", 30)hashCode()相同但equals()为false—— 允许;但若它们equals()为true却哈希不同,就违反契约 - 安全做法:用所有参与
equals()比较的字段共同计算哈希,例如Objects.hash(name, age) - 性能提示:字段越多、计算越复杂,哈希值分布通常越均匀,冲突越少;但过度复杂(如深遍历集合)反而拖慢插入/查找
正确重写的最小实操模板
别手写哈希算法。用 JDK 提供的工具类,既安全又简洁:
立即学习“Java免费学习笔记(深入)”;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // ✅ 自动处理 null,顺序敏感,字段一致
}
}
- 检查顺序很重要:先
this == o(引用相等),再判空和类型,最后比字段 -
Objects.equals(a, b)安全处理null,比a.equals(b)直接调用更鲁棒 - 字段选择必须严格对齐:哪个字段参与
equals(),就必须出现在Objects.hash(...)里 - 如果类可变(字段后续会修改),且该对象已放入
HashSet或作为HashMap的 key,行为未定义——哈希值变了,桶位置就错了
最容易被忽略的一点:当父类已重写 equals() 和 hashCode(),子类若新增字段并参与比较,就必须在子类中**同时重写两者**,并在实现中显式调用 super.equals() 和 super.hashCode(),否则契约断裂。






