HashSet底层复用HashMap的key唯一性机制实现去重:元素作为key存入,value统一为静态占位对象PRESENT;add(e)本质是map.put(e, PRESENT),去重依赖hashCode()和equals()判断。

HashSet 底层并不直接使用 HashMap 的“键值对”来存元素,而是把元素作为 HashMap 的 key,value 固定用一个共享的 Object(比如 `PRESENT`)占位。去重的本质,是利用 HashMap 对 key 的唯一性约束来实现的。
为什么用 HashMap 的 key 而不是 value?
HashMap 的 key 不允许重复,插入时会通过 hashCode() 和 equals() 判断是否已存在相同 key;而 value 没有这个限制。HashSet 只关心“有没有这个元素”,不关心“存了什么值”,所以把元素当 key 最自然、最高效。
- 每次调用
add(e),实际执行的是map.put(e, PRESENT) != null - 如果 key 已存在,
put()返回旧 value(即PRESENT),整个表达式结果为true,说明添加失败(已存在) - 如果 key 不存在,
put()返回null,表达式为false,表示添加成功
去重依赖两个关键方法:hashCode() 和 equals()
HashMap 在判断 key 是否重复时,分两步:
- 先算
e.hashCode(),定位到哈希桶(数组下标) - 再在该桶的链表或红黑树中,用
e.equals(existingKey)逐个比较
所以,若自定义类放入 HashSet,必须重写 hashCode() 和 equals(),且要保持逻辑一致:相等的对象必须有相同的哈希码。
空值也能去重,靠的是 HashMap 的特殊处理
HashMap 允许一个 key 为 null,它被固定放在数组索引 0 的位置,并单独用 == 判断是否为 null(不调用 equals())。因此 HashSet 也只允许一个 null 元素——第二次 add(null) 会被判定为重复。
不是“用 HashMap 实现了去重逻辑”,而是“复用了 HashMap 的去重机制”
HashSet 没有自己写哈希计算、冲突解决或扩容逻辑,它把所有核心工作都委托给内部的 HashMap 实例。它的 add、contains、remove 等方法,几乎都是对 map 的简单封装。这种设计避免重复造轮子,也保证了性能和稳定性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











