重写 equals 时必须同步重写 hashcode,否则 hashmap、hashset 等哈希表操作会失效;相等对象必须有相同哈希码,且 equals 需满足自反性、对称性、传递性、一致性与 null 安全。

重写 equals 时必须同步重写 hashCode,否则 HashMap、HashSet 等哈希表结构会失效——比如对象明明相等却查不到、删不掉,或重复插入。
核心原则:相等的对象必须有相同的哈希码
Java 规范强制要求:只要 a.equals(b) == true,就一定有 a.hashCode() == b.hashCode()。这是哈希表定位桶(bucket)的基础逻辑。违反它,集合的存、取、删都会出错。
- 只改
equals不改hashCode→ 两个逻辑相等的对象被散列到不同桶中 → 查找失败 -
hashCode漏掉equals中用到的某个字段 → 相等对象算出不同哈希值 → 集合行为不可预测 - 用可变字段(如后期会修改的 List 或 Date)参与
hashCode计算 → 对象入集合后哈希值变化 → 永远找不到自己
equals 方法要满足五项基本约定
确保比较逻辑可靠,避免对称性、传递性等被破坏:
-
自反性:非 null 对象调用
x.equals(x)必须返回true -
对称性:若
x.equals(y)为true,则y.equals(x)也得是true -
传递性:若
x.equals(y)和y.equals(z)都为true,则x.equals(z)也必须为true - 一致性:只要参与比较的字段没变,多次调用结果不能变
-
对 null 安全:任何非 null 对象调用
x.equals(null)必须返回false
推荐用 getClass() == obj.getClass() 判断类型,比 instanceof 更稳妥,尤其在类可被继承时能避免对称性问题。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
hashCode 实现推荐用 Objects.hash()
一行代码解决多数问题,底层已处理 null、数组、浮点数等边界情况:
- 只包含
equals中实际参与比较的字段,且顺序保持一致 - String、包装类等引用类型:直接传入
Objects.hash(name, age, status)即可 - 数组字段:用
Arrays.hashCode(arr),别用arr.hashCode()(那是引用哈希) - float/double:用
Float.floatToIntBits(f)或Double.doubleToLongBits(d),避免 -0.0 和 0.0 哈希不等 - 避免使用时间戳、随机数、
System.identityHashCode()等不稳定值
一个安全的 Person 示例
(字段 name、age 不可变,无继承需求)
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
这个写法覆盖了所有关键点:类型严格检查、null 安全、字段一致、不可变参与、简洁可读。只要字段不变,哈希值就不变;只要 equals 成立,hashCode 就一定相同。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










