HashSet底层完全基于HashMap实现,其元素作为HashMap的key存储,value统一为静态对象PRESENT;所有操作(add/remove/contains等)均委托给HashMap执行,复用其哈希冲突处理、扩容等成熟机制。

HashSet 底层确实完全依赖 HashMap 实现,它本身不维护独立的哈希表结构,而是把所有元素作为 HashMap 的 key 存储,value 固定用一个共享的静态 final 对象(`PRESENT`)占位。
为什么用 HashMap 而不是自己实现哈希表?
复用成熟、高效、经过充分测试的 HashMap,避免重复造轮子。HashMap 已经解决了哈希冲突(拉链法 + 红黑树)、扩容机制、线程安全边界等复杂问题。HashSet 只需专注“去重”这一语义,把底层数据结构委托给 HashMap 即可。
HashSet 的核心字段和构造逻辑
查看 JDK 源码(如 OpenJDK 8+),会发现 HashSet 内部只有一个关键成员:
private transient HashMap<e object> map;</e>
构造方法实际是初始化这个 map:
- 无参构造:`map = new HashMap()`
- 指定初始容量:`map = new HashMap(initialCapacity)`
- 传入已有 Collection:逐个调用 `map.put(e, PRESENT)`
add、remove、contains 等操作如何映射到 HashMap?
所有行为都转为对 map 的 key 操作,value 始终是同一个对象 `PRESENT`(定义为 `private static final Object PRESENT = new Object();`):
- add(E e) → `map.put(e, PRESENT)`,返回值为 `null` 表示新增成功(原无该 key),否则说明已存在
- remove(Object o) → `map.remove(o)`,返回非 null 表示删除成功
- contains(Object o) → `map.containsKey(o)`
- size() → 直接返回 `map.size()`
- iterator() → 返回 `map.keySet().iterator()`,遍历的是 keySet 视图
注意点:null 元素的支持与一致性
因为基于 HashMap,HashSet 允许且仅允许一个 null 元素 —— 这对应 HashMap 中 key 为 null 的唯一键值对。`map.put(null, PRESENT)` 合法,第二次 put 会覆盖 value(但 value 总是 PRESENT),所以逻辑上仍保证 null 只出现一次。
这种设计简洁而高效,没有额外存储开销,也没有语义冗余。理解这一点,也就看懂了 Java 集合框架中“组合优于继承”的典型实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











