必须重写hashcode()和equals(),因为hashmap先用hashcode()定位桶,再用equals()在桶内比对;若不重写,逻辑相等对象因默认引用比较或哈希值不同而无法识别为同一key。

Java 中自定义对象作为 HashMap 的 key,必须重写 hashCode() 和 equals(Object) 两个方法。否则,即使两个对象逻辑上相等(如字段值完全相同),HashMap 也无法正确识别它们为同一个 key,导致重复插入、查不到值、删除失败等问题。
为什么必须重写这两个方法?
HashMap 查找 key 时,先用 hashCode() 计算哈希值,定位到桶(bucket);再用 equals() 在该桶的链表或红黑树中逐个比对。如果只重写其中一个:
- 只重写
hashCode():不同对象可能返回相同哈希值(哈希冲突正常),但equals()默认是引用比较,导致逻辑相等的对象被当成不同 key; - 只重写
equals():不同对象可能因默认hashCode()返回不同值,被分到不同桶里,根本不会触发equals()比较,查找直接失败。
重写原则:保持一致性
只要 equals() 返回 true,两个对象的 hashCode() 就必须相同;反之不强制(哈希值相同不等于相等)。这是 Java 规范的硬性要求。
建议操作:
- 在
equals()中,只使用 影响逻辑相等性的字段(即哪些字段决定“这个对象是否等于另一个”); - 在
hashCode()中,**必须使用和equals()完全相同的字段**计算哈希值; - 避免在
hashCode()或equals()中使用可变字段(如后续会修改的属性),否则对象放入HashMap后若字段变更,哈希位置错乱,key 变成“不可达”。推荐 key 类设计为不可变(final 字段 + 无 setter)。
一个安全示例(不可变 Person 类)
(使用 IntelliJ / Eclipse 自动生成,或手动编写)假设用姓名和年龄判断 Person 是否相等:
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = 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); // 与 equals 使用的字段严格一致
}
}
这样,以下代码能正常工作:
Map<person string> map = new HashMap();
map.put(new Person("Alice", 25), "Engineer");
System.out.println(map.get(new Person("Alice", 25))); // 输出 "Engineer"</person>
常见踩坑点
-
字段类型含 null 风险:用
Objects.equals(a, b)和Objects.hash(...)安全处理 null,别直接调a.equals(b)或a.hashCode(); -
继承场景未考虑父类字段:子类重写时,若父类已有重要字段参与 equals/hashCode,需显式调用
super.equals()和super.hashCode(); - IDE 自动生成后忘记检查字段一致性:有时勾选了多余字段,或删了字段但没同步更新两个方法;
-
用了 Lombok?注意 @EqualsAndHashCode:默认只包含非静态、非 transient 字段;若需指定字段,加
of = {"name", "age"};若含父类字段,加callSuper = true。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











