必须同时重写 equals 和 hashCode 且基于同一组属性计算,否则在 HashSet、HashMap 中必然出错:该去重的不去重、该查到的查不到。

直接说重点:要让自定义对象在逻辑上正确相等,必须同时重写 equals 和 hashCode,且二者基于**同一组属性**计算,否则在 HashSet、HashMap 等集合中会出错——比如该去重的没去重,该查到的查不到。
什么时候必须重写这两个方法
当你希望按内容判断对象是否相等,而不是看它们是不是同一个内存地址时,就需要重写。典型场景包括:
- 把对象放进
HashSet或作为HashMap的 key,要求自动去重或精准查找 - 业务上认为两个
User对象只要id相同就算同一个用户 - 比较两个
Point(x, y)是否代表同一坐标点 - 单元测试中用
assertEquals断言自定义对象是否符合预期
重写 equals 的关键步骤
以一个 Person 类为例,假设只用 id 和 name 判断相等:
- 先判引用相等:
if (this == obj) return true; - 再判 null 和类型:
if (obj == null || getClass() != obj.getClass()) return false; - 强转后逐字段比较,推荐用
Objects.equals(a, b)处理 null 安全(如Objects.equals(name, other.name)) - 所有用于 equals 的字段,后续也必须参与 hashCode 计算
重写 hashCode 的常见写法
不能随便返回一个固定数,也不能只用一个字段。标准做法是组合关键字段的哈希值:
立即学习“Java免费学习笔记(深入)”;
- 用
Objects.hash(field1, field2, ...)最省事也最安全(JDK 7+) - 手动写法示例:
return Objects.hashCode(id) * 31 + Objects.hashCode(name);(31 是常用质数,减少冲突) - 确保:只要
equals返回true,这个公式算出的值就必须完全一样
不重写或只重写一个的后果
后果不是“可能出错”,而是“一定出错”,只是时机问题:
- 只重写
equals不重写hashCode:对象放进HashMap后,get(key)可能永远返回null,因为哈希桶找错了位置 - 只重写
hashCode不重写equals:两个内容相同的对象仍被判定为不等,HashSet会存两份 - 两个方法用不同字段:比如
equals比id+name,但hashCode只算id,一旦name不同但id相同,就违反契约


















