HashSet 底层直接复用 HashMap 实现,其所有操作均委托给内部 HashMap 实例执行,去重完全依赖 HashMap 的 key 唯一性约束,构造参数、扩容、树化等行为与 HashMap 完全一致。

HashSet 和 HashMap 在底层原理上不是“相似”或“借鉴”,而是直接复用——HashSet 的整个存储和去重逻辑,完全由内部持有的一个 HashMap 实例承担。
HashSet 就是 HashMap 的一层薄包装
查看 JDK 源码就能确认:HashSet 类里声明了一个私有字段:
private transient HashMap<E, Object> map;它不自己管理数组、不处理哈希计算、不实现扩容逻辑,所有 add/remove/contains 操作,都转交给这个 map 执行:
-
add(e) → 调用
map.put(e, PRESENT),利用 HashMap 键的唯一性保证元素不重复 -
remove(o) → 调用
map.remove(o),返回值是否等于 PRESENT 判断是否删除成功 -
contains(o) → 调用
map.containsKey(o),查的是键是否存在
去重机制完全依赖 HashMap 的 Key 约束
HashSet 本身没有定义任何哈希或比较逻辑。它的去重能力,100% 来自 HashMap 对 key 的要求:
立即学习“Java免费学习笔记(深入)”;
- 同一个桶中,两个 key 被认为相等,必须满足
hashCode() 相等 && equals() 返回 true - 只要你的元素正确重写了
hashCode()和equals(),HashMap 就能正确判断重复 —— HashSet 自然也就可靠 - 如果没重写,比如用自定义类但没改这两个方法,即使内容相同也会被当作不同元素存入
结构共享,参数共用
HashSet 构造时传入的初始容量和负载因子,最终都会传递给内部的 HashMap:
- 无参构造:底层 HashMap 默认容量 16,负载因子 0.75,阈值 12
- 指定容量构造:
new HashSet(20)→ 内部 HashMap 容量取最近的 2 的幂(32),阈值 = 32 × 0.75 = 24 - 扩容、树化(链表转红黑树)、退化(红黑树转链表)等全部行为,和 HashMap 完全一致
为什么这样设计?组合优于继承
Set 关注“元素唯一”,Map 关注“键唯一”,语义高度契合。Java 不采用继承 HashMap 的方式,而是用组合模式:
- 避免破坏 Map 接口契约(比如 Set 不需要 value)
- 复用已验证的哈希表实现,减少冗余代码和维护成本
- 保持接口职责清晰:HashSet 对外只暴露 Set 接口,内部细节完全隐藏


















