只重写equals会导致HashSet找不到对象,因为HashSet先用hashCode定位桶再用equals比对;若逻辑相等的对象哈希值不同,就会散列到不同桶中,造成contains返回false、add重复对象成功等现象。
equals 会导致 HashSet 找不到对象?因为 HashSet(以及 HashMap)先用 hashCode 定位桶,再用 equals 精确比对。如果两个逻辑相等的对象(u1.equals(u2) == true)返回不同哈希值,它们大概率被散列到不同桶里——set.contains(u2) 就会返回 false,哪怕 u1 已在集合中。
contains 返回 false;add 同一对象两次,集合大小变成 2hashCode 仍走 Object 默认实现(基于内存地址),而 equals 已按业务字段比较equals 和 hashCode 的契约到底约束什么?Java 规范只强制一条核心规则:如果 a.equals(b) == true,那么 a.hashCode() == b.hashCode() 必须为 true。反过来不成立(哈希相同 ≠ 一定相等)。
HashMap.get(key) 可能返回 null,即使键确实存在equals 自身要求,但若没同步更新 hashCode,这些也白搭getClass() != obj.getClass() 做类型检查(影响继承),改用 instanceof 更安全别自己手算哈希或拼字符串,用 JDK 提供的工具链保底。
equals 中只比较「不可变」或「参与业务判等」的字段,比如 id 或 name,别把 lastLoginTime 这种可变字段塞进去hashCode 必须用和 equals 完全相同的字段计算,推荐 Objects.hash(id, name) ——它自动处理 null,且结果确定HashSet 或当了 HashMap 的 key,就别再修改参与 hashCode 计算的字段,否则永远找不回来IntelliJ 或 Eclipse 生成的 equals/hashCode 大多合规,但容易忽略两个隐形坑:
createTime,但业务上只认 id,结果时间戳微差就判不等getClass() 严格类型检查,导致子类对象无法和父类实例 equals(比如 AdminUser 和 User)LazyInitializationException 场景),getter 可能抛异常 —— 此时应改
真正难的从来不是“怎么写”,而是“哪些字段该进判等逻辑”,这得抠清楚业务语义,而不是堆代码。