重写 hashcode 和 equals 必须同步进行,否则会导致哈希集合中对象“查不到”“删不掉”等异常;因哈希集合先用 hashcode 定位桶,再用 equals 比对,二者字段必须严格一致,且避免使用可变字段。
重写 hashcode 和 equals 必须同步进行,否则对象放进 hashset、hashmap 等哈希集合后,可能出现“查不到”“删不掉”“重复添加”甚至“凭空消失”的问题——这不是 bug,是契约被破坏的必然结果。
为什么必须一起重写
哈希集合依赖两步定位:
- 先用
hashCode()决定对象存进哪个桶(bucket) - 再在该桶内用
equals()逐个比对找目标
如果只重写 equals(),两个内容相同的对象因默认 hashCode() 返回不同内存地址值,会被分到不同桶里——contains() 或 get() 根本不会去另一个桶里找,自然返回 false 或 null。
字段选择必须严格一致
参与 equals() 判断的每一个字段,都必须参与 hashCode() 计算;反之,hashCode() 多加字段虽不报错,但可能引入逻辑歧义。
- 比如
Person类中,equals()比较name和age,那hashCode()就不能只用name - 漏掉
age→ 相等对象哈希值不同 → 违反“相等必同哈希”原则 - 用了可变字段(如后续会修改的
status)→ 对象加入HashSet后改了status→ 哈希值变化 → 原桶里找不到它,新桶里又没重新放入 → “丢失”
写法上要兼顾安全与简洁
手写容易出错,推荐用标准工具和惯用写法:
- 用
Objects.equals(a, b)替代a.equals(b),自动处理null,避免空指针 - 用
Objects.hash(field1, field2, ...)生成哈希值,内部已做null安全处理 - 类型判断用
instanceof,不要直接强转;继承场景下,子类hashCode()应包含super.hashCode() - 优先基于
final字段计算,从根源规避可变性风险
常见失效现象与自查点
遇到集合行为异常时,快速核对这几项:
-
set.add(p1); set.contains(p2)返回false,但p1.equals(p2) == true→ 很可能hashCode没重写,或字段不一致 - 同一个对象反复
add进HashSet,结果大小持续增长 →hashCode每次返回不同值(比如用了System.nanoTime()或可变字段) - JPA 实体类误把数据库主键
id加入equals→ 新建对象id为null,导致所有新对象互不相等 → 去重失效










