重写 equals 和 hashcode 是使用哈希集合的强制要求,二者必须协同:若 equals 为 true,则 hashcode 必须相同;equals 需满足自反性、对称性、传递性、一致性与非空性;推荐用 objects.equals() 和 objects.hash() 统一处理。

重写 equals 和 hashCode 不是“可选优化”,而是使用 HashMap、HashSet 等集合时的强制要求。二者必须协同设计,否则对象在哈希结构中会“消失”或“重复”——这不是偶发 Bug,而是契约被破坏后的必然结果。
equals 方法必须满足五项基本约定
重写前先确认逻辑是否符合 Java 规范定义的语义一致性:
-
自反性:任何非 null 对象
x,x.equals(x)必须返回true -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true -
传递性:若
x.equals(y)和y.equals(z)都为true,则x.equals(z)也应为true - 一致性:只要参与比较的字段未变,多次调用结果必须稳定
-
非空性:对任意非 null 对象
x,x.equals(null)必须返回false
hashCode 必须与 equals 严格同步
核心规则只有一条:如果两个对象 equals 返回 true,它们的 hashCode 值必须完全相同。反之不成立(哈希冲突允许)。实现时注意:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 只使用
equals中实际参与比较的字段计算哈希值,多一个或少一个都会导致不一致 - 避免使用可变字段(如后期可能修改的
name),否则对象放入HashSet后再改字段,就再也找不到了 - 字段含
null时,不要直接调用field.hashCode(),改用Objects.hashCode(field)安全兜底 - 数组字段需用
Arrays.hashCode(arr),不能用arr.hashCode()(那是引用哈希) - 浮点数用
Float.floatToIntBits(f)或Double.doubleToLongBits(d),避免-0.0和0.0哈希不同
推荐统一写法:用 Objects 工具类收口
手动拼接哈希值容易出错,建议全部交给标准工具类处理:
-
equals中统一用Objects.equals(a, b)比较字段,自动处理null -
hashCode中统一用Objects.hash(f1, f2, f3),内部已适配null、数组、浮点等场景 - 类型判断用
getClass() == obj.getClass(),不用instanceof,防止子类打破对称性 - 开头保留
this == obj和obj == null快速路径,提升高频调用性能
重构后务必验证一致性
光写完不测试等于没做。加几个关键断言就能守住底线:
- 新建两个字段值完全相同的对象,验证
a.equals(b) && a.hashCode() == b.hashCode() - 把对象加入
HashSet,再用另一个相等对象调用set.contains(),必须返回true - 作为
HashMap的 key 存入后,用相等对象get(),必须能取到值 - 字段为
null时运行不抛NullPointerException
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










