必须重写equals()和hashCode()方法,因为HashMap先用hashCode()定位桶再用equals()比对;若未重写,默认引用比较和内存地址哈希会导致逻辑相等的对象无法被正确查找或存储。

在 Java 中,要让自定义对象能作为 HashMap 的键(key),关键不是“重写 HashMap”,而是**正确实现该对象的 equals() 和 hashCode() 方法**。否则即使对象逻辑上相等,HashMap 也无法正确查找、存储或覆盖。
为什么必须重写 equals() 和 hashCode()
HashMap 查找键时,先用 hashCode() 定位到桶(bucket),再用 equals() 在该桶内逐个比对。如果两个逻辑相等的对象返回不同哈希值,它们会被散列到不同桶中,get() 就永远找不到;如果哈希值相同但 equals() 返回 false,也会被当作不同键处理。
默认的 Object.equals() 是引用比较,Object.hashCode() 通常返回内存地址哈希——这对自定义对象完全不可用。
如何正确重写这两个方法
以一个表示学生的类为例:
立即学习“Java免费学习笔记(深入)”;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 1. equals():判断逻辑是否相等
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
// 2. hashCode():用相同字段参与计算,且与 equals 逻辑一致
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
- 使用
Objects.equals()和Objects.hash()可避免空指针和手写错误 - 参与
equals()判断的字段,必须全部参与hashCode()计算 - 字段一旦用于
hashCode()和equals(),就应尽量设为final,避免键在放入 Map 后被修改(否则哈希值变化,导致无法找回)
验证是否生效
测试代码示例:
Map<Student, String> map = new HashMap<>();
Student s1 = new Student("Alice", 20);
Student s2 = new Student("Alice", 20); // 逻辑相同,但不同对象
map.put(s1, "A");
System.out.println(map.get(s2)); // 输出 "A",说明正常工作
若没重写方法,这里会输出 null。
进阶提醒:键对象应尽量不可变
如果 Student 的 name 或 age 能被修改(比如有 setName()),而它已被放入 HashMap,后续修改会导致其哈希值改变,原位置找不到,新位置又没存值——这个键就“丢失”了,且无法被 remove() 或 containsKey() 正确识别。
- 推荐将键类设计为不可变:字段
final,不提供 setter,构造器初始化后不再变更 - 如必须可变,请确保修改前先
remove(),改完再put(),但极易出错,不建议


















