必须同时重写equals与hashCode且逻辑一致:先用hashCode定位桶,再用equals比对;equals需满足五条规范,hashCode须基于相同字段计算;对象加入集合后不可修改参与比较的字段。

要让自定义对象在 HashSet、HashMap、LinkedHashSet 等哈希集合中正确去重或定位,equals 与 hashCode 必须成对重写,且严格保持逻辑一致。核心不是“写出来”,而是“写对”——稍有偏差,集合就会表现异常:重复添加、查不到、删不掉。
必须同时重写,缺一不可
哈希集合的工作流程是:先用 hashCode() 定位桶(bucket),再在桶内用 equals() 逐个比对。两者脱节,整个流程就断了:
- 只重写
equals不重写hashCode→ 逻辑相等的对象可能被分到不同桶,equals根本没机会执行,结果就是重复存入; - 只重写
hashCode不重写equals→ 默认仍按内存地址比较,两个字段相同的对象仍被视为不同; -
hashCode每次返回随机数或依赖可变字段 → 同一个对象插入后无法再被找到,相当于“丢进黑洞”。
equals 要守住五条基本线
它不只是“比字段”,更是对“相等”这个语义的严谨定义:
- 先用
this == obj快速判断是否同一引用; - 接着判
obj == null和类型(推荐用getClass() == obj.getClass(),避免子类误判); - 关键字段逐个比较:基本类型用
==,引用类型统一用Objects.equals(a, b)(自动处理null,安全省心); - 确保满足自反性、对称性、传递性、一致性,以及对
null调用必返回false。
hashCode 必须和 equals “用同一套字段”
哈希值不是随便算的数字,它是 equals 判定逻辑相等的“指纹依据”:
- 所有参与
equals比较的字段,必须全部参与hashCode计算; - 没参与比较的字段(如数据库 ID、临时状态、时间戳、随机生成值)绝不能加入;
- 数组字段要用
Arrays.hashCode(arr),不能直接调arr.hashCode(); - 强烈推荐
Objects.hash(name, age, email)—— 它内部已做null安全处理,分布合理,不易出错。
对象放进集合后,别动参与比较的字段
一旦对象被加入 HashSet 或作为 HashMap 的 key,它当时的 hashCode 就决定了它在哈希表里的“住址”。如果之后修改了 name 或 age 这类参与 equals 和 hashCode 的字段:
- 它的哈希桶位置不会自动更新;
- 后续
contains、remove都会失败——对象还在集合里,但你再也“找不见”了; - 解决办法:把这类字段设计为
final,或确保业务逻辑中不修改它们。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











