hashset通过hashmap的key唯一性保证元素唯一,依赖对象的hashcode()和equals()协同判断;自定义类必须重写二者,且应避免使用可变字段参与哈希计算。

HashSet 保证元素唯一性,靠的是底层 HashMap 的 key 唯一机制,再叠加对象自身的 hashCode() 和 equals() 协同判断。
底层用 HashMap 存储,value 是固定占位符
HashSet 自身不直接存数据,而是内部持有一个 HashMap<e object></e> 字段。每次调用 add(e),实际是执行 map.put(e, PRESENT)。由于 HashMap 的 key 必须唯一,重复的 key 会被覆盖——而 value 全部指向同一个静态 final 对象 PRESENT,不占额外内存。
插入时先算 hash,再用 equals 精确比对
当添加一个新元素时:
- 先调用该元素的
hashCode()方法,得到哈希值,并通过(n - 1) & hash定位到数组中的桶位置(n 是数组长度,必须为 2 的幂) - 如果桶为空,直接封装成 Node 放入
- 如果桶非空,遍历链表或红黑树中的每个已有元素,对每个元素调用
equals()方法和新元素比较 - 只要有一次
equals()返回 true,就认定重复,add()返回 false,不插入
自定义类必须重写 hashCode 和 equals
如果把自定义对象放进 HashSet,但没重写这两个方法:
- 默认
hashCode()返回对象内存地址的整数表示,不同实例几乎一定不同 → 即使逻辑相同也被当成不同元素 - 默认
equals()比较的是引用地址 → 两个内容相同的对象也判为不等 - 结果就是:本该去重的没去重,或者因哈希分布不合理导致性能骤降
所以必须保证:两个 equals() 返回 true 的对象,hashCode() 也必须返回相同值;反过来不强制,但强烈建议避免大量哈希碰撞。
可变对象放进去后改字段,可能引发“丢失”问题
一旦对象被加入 HashSet,后续如果修改了影响 hashCode() 计算的字段:
- 它原本所在的桶位置就“找不到了”,
contains()或remove()可能失效 - 因为查找时会重新算 hash,去另一个桶里找,而原桶里还留着它(变成“不可达但存在”的状态)
- 这不是 bug,是设计使然——HashSet 不负责监控对象内部变化
因此,作为 HashSet 元素的对象,最好设计为不可变(immutable),或至少确保参与 hashCode/equals 的字段在加入后不再修改。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











