结论:重写 equals() 时必须同步重写 hashcode(),这是 java 规范强制契约;否则哈希集合(如 hashmap、hashset)会因散列不一致导致重复插入或查找失败。

直接说结论:不是“Object 类必须同时重写”,而是当你重写 equals() 方法时,必须同步重写 hashCode() 方法——这是 Java 规范强制要求的契约,否则会破坏哈希集合(如 HashMap、HashSet)的正常行为。
这个要求和 Object 类本身无关,而是源于 Object 中定义的两个方法之间的逻辑约束。Object 默认实现里,equals() 比较内存地址,hashCode() 也基于内存地址生成整数,二者天然一致。但一旦你为了业务逻辑重写 equals()(比如按姓名+年龄判断 Person 是否相等),就打破了这种一致性,必须手动让 hashCode() 与之匹配。
为什么必须保持 equals 和 hashCode 一致?
哈希集合依赖两者协同工作:
- 先用
hashCode()快速定位桶(数组索引) - 再用
equals()在桶内精确比对
如果只改 equals() 不改 hashCode():
- 两个逻辑相等的对象(
p1.equals(p2) == true)可能被散列到不同桶中 -
HashSet.add(p2)会当作新元素插入 → 出现重复 -
HashMap.get(p2)找不到已存的p1→ 返回null
这违反了集合的基本语义,不是 bug,是设计失效。
契约原文来自 Java 官方文档
If two objects are equal according to the
equals(Object)method, then calling thehashCode()method on each of the two objects must produce the same integer result.
即:只要 a.equals(b) 为 true,就必须保证 a.hashCode() == b.hashCode()。
这不是建议,是所有标准集合类(HashMap、HashSet、ConcurrentHashMap 等)运行的前提。IDE(如 IntelliJ)和静态检查工具(如 SonarQube)会直接报错,因为这是可检测的契约违规。
怎么写才安全?
确保 hashCode() 的计算字段和 equals() 的判断字段完全一致:
- ✅ 正确:
equals()比较name和age→hashCode()用Objects.hash(name, age) - ❌ 错误:
equals()比较name和age,但hashCode()还多用了id→ 可能equals()为true而hashCode()不同 - ⚠️ 低效:
hashCode()漏掉age→ 所有同名不同龄的人都挤进同一个桶,退化成链表遍历
推荐写法(JDK 7+):
@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); // 字段顺序和 equals 一致
}
不重写的实际后果举例
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
Set<person> set = new HashSet();
set.add(p1);
System.out.println(set.contains(p2)); // 输出 false(预期应为 true)</person>
原因:p1 和 p2 equals() 返回 true,但默认 hashCode() 返回不同值 → p2 查找时去了错误的桶,根本没机会触发 equals() 判断。
不复杂,但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











