hashset去重需hashcode()与equals()协同工作:先用hashcode()定位桶,再用equals()精确比较;若不重写hashcode(),相同内容对象因哈希值不同被分至不同桶,equals不被调用,导致重复存入。

因为 HashSet 依赖哈希值快速定位存储位置,再用 equals 判断是否真正重复;如果不重写 hashCode(),两个内容相同但地址不同的对象会算出不同哈希值,被分到不同桶里,equals 根本不会被调用,导致重复存入。
HashSet 去重靠两步配合
它不是只看 equals,也不是只看 hashCode,而是分阶段协作:
- 第一步:调用对象的
hashCode(),算出一个整数,决定该对象放进哪个“桶”(数组索引位置) - 第二步:如果那个桶里已有元素,才逐个调用
equals()比较内容是否真的一样
不重写 hashCode 的后果
默认的 hashCode() 基于内存地址,哪怕两个 Student 都叫“李四”、年龄 20,只要不是同一个 new 出来的实例,哈希值就不同:
- 它们会被分配到 HashSet 底层 HashMap 的不同数组位置
- 彼此根本不会触发 equals 对比
- 结果:集合里存了两个逻辑上相同的对象,size 变成 2
为什么 equals 和 hashCode 必须一起改
Java 规定:如果 obj1.equals(obj2) == true,那么 obj1.hashCode() == obj2.hashCode() 必须成立。这是哈希集合正常工作的契约:
- 只重写 equals 而不重写 hashCode → 违反契约,HashSet、HashMap 等容器行为不可预测
- 两者都按相同业务字段(如 id、name + age)计算 → 同内容对象进同一桶,再靠 equals 精确去重
怎么写才安全
推荐用 IDE 自动生成(如 IntelliJ 的 Alt + Insert),避免手写错误。核心原则是:
-
hashCode()中参与计算的字段,必须和equals()中用于比较的字段完全一致 - 所有参与比较的字段都不能为 null,或在计算前做空值处理(如
Objects.hash(id, name, age)) - 一旦对象加入 HashSet,尽量别修改影响 hashCode 的字段,否则可能再也找不到了
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











