必须重写自定义key类的equals和hashCode:HashMap先用hashCode定位桶,再用equals精确匹配;若不重写,逻辑相等的对象可能散列到不同桶导致查不到,或哈希碰撞误判引发重复插入;须满足相等性(equals为true则hashCode必等)和一致性(同一对象多次hashCode返回相同值)原则。

HashMap 本身不需要你重写 equals 和 hashCode,真正需要重写的是**作为 key 的自定义类**。冲突(哈希碰撞)无法完全避免,但合理实现 equals 和 hashCode 能让 HashMap 正确工作、减少无效碰撞、保障查找效率。
为什么 key 类必须重写 equals 和 hashCode?
HashMap 查找 key 时:先用 hashCode() 定位桶(bucket),再用 equals() 在该桶的链表/红黑树中精确比对。如果只重写 equals 不重写 hashCode,两个逻辑相等的对象可能散列到不同桶,导致 get() 找不到;反之,若 hashCode 相同但 equals 返回 false,会误判为不同 key,引发重复插入或覆盖错误。
重写的两个核心原则
一致性原则:只要对象参与比较的字段没变,多次调用 hashCode() 必须返回相同值。
相等性原则:如果 a.equals(b) == true,那么 a.hashCode() == b.hashCode() 必须成立。
违反任一原则,HashMap 行为不可预测(如 containsKey 返回 false,get 返回 null,即使 key 确实存在)。
怎么正确重写(以 Person 类为例)
假设 Person 用 name 和 age 唯一标识:
立即学习“Java免费学习笔记(深入)”;
- 在
equals()中:先做 null 和类型检查,再逐个比较关键字段(推荐用Objects.equals(a, b)处理 null) - 在
hashCode()中:只使用equals中用到的相同字段,推荐用Objects.hash(name, age)生成,它自动处理 null 并保证一致性 - 字段必须是不可变的(如 final),或确保在放入 HashMap 后不再修改——否则 hash 值变化会导致 key “丢失”(再也查不到)
示例片段:
public 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,忘了hashCode(最常见) - 在
hashCode中用了可变字段(比如加了lastLoginTime),导致 key 放入后修改字段,后续get失败 -
equals比较了无关字段(如 id 是数据库自增主键),而hashCode没包含它,造成逻辑不一致 - 用随机数、当前时间等不稳定值参与
hashCode计算 - IDE 自动生成的代码未检查字段是否真的参与业务意义上的“相等判断”
不复杂但容易忽略:只要这个类可能当 HashMap(或 HashSet、HashTable、ConcurrentHashMap)的 key 或元素,就必须严格遵守这两方法的契约。


















