HashSet 与 HashMap 关系
面试官问:"HashSet 底层是怎么实现的?"
候选人小张答:"HashSet 底层用的是 HashMap。"
面试官追问:"具体是怎么用 HashMap 实现的?"
小张说不上来。
【面试官心理】 HashSet 依赖 HashMap 实现是 Java 集合框架的经典设计。能说出 HashSet 用 HashMap 存储元素作为 key、固定 value 作为占位符的候选人,说明对 JDK 源码有过研究。
一、HashSet 的本质 🔴
1.1 源码结构
1.2 为什么 value 是固定对象
1.3 所有操作都委托给 HashMap
二、HashSet vs HashMap 对比 🔴
2.1 核心区别
2.2 等价关系
2.3 ❌ 错误示范
候选人原话:"HashSet 和 HashMap 是两个完全不同的实现。"
问题诊断:
- 没有看过 HashSet 的源码
- 不理解集合框架的设计模式
面试官内心 OS:"HashSet 源码只有几十行,readability 很高。这个候选人肯定没看过。"
【面试官心理】 HashSet 依赖 HashMap 实现是 JDK 的经典设计模式。理解这一点,说明候选人对集合框架有深入理解。
三、HashSet 的构造方法 🟡
3.1 四种构造方法
3.2 LinkedHashSet
3.3 TreeSet
四、自定义对象作为 HashSet 元素 🟡
4.1 必须重写 hashCode 和 equals
4.2 ❌ 错误示范
4.3 常见坑点
自定义对象作为 HashSet 的元素时,必须同时重写 hashCode() 和 equals(),并且保持一致性:相等的对象必须有相同的 hashCode。
五、生产选型 🟡
5.1 Set 类型选择
5.2 性能对比
5.3 最佳实践
六、源码扩展 🟢
6.1 Clone 方法
6.2 序列化
6.3 Iterator
七、面试高频追问 🟡
7.1 第一层追问
面试官:"HashSet 的 contains 操作时间复杂度是多少?"
候选人:...
正确回答:O(1) 均摊。因为 HashSet 的 contains 委托给 HashMap 的 containsKey,而 HashMap 的 containsKey 是 O(1) 均摊。
7.2 第二层追问
面试官:"HashSet 的 add 操作怎么保证元素不重复?"
候选人:...
正确回答:HashSet 的 add 委托给 HashMap 的 put。HashMap 要求 key 唯一,底层通过 hashCode 定位桶,再通过 equals 比较 key。所以:
- hashCode 决定在哪个桶
- equals 决定是否相同
7.3 第三层追问
面试官:"HashSet 和 HashMap 的内部结构有什么关系?"
候选人:...
正确回答:
- HashSet 内部持有一个 HashMap
- HashSet 的元素作为 HashMap 的 key
- HashMap 的 value 是一个固定的占位符(PRESENT)
- 所有操作都委托给 HashMap
7.4 第四层追问
面试官:"LinkedHashSet 和 HashSet 的区别是什么?"
候选人:...
正确回答:
- LinkedHashSet 继承 HashSet
- LinkedHashSet 底层用 LinkedHashMap 代替 HashMap
- LinkedHashSet 按插入顺序遍历,HashSet 无序
【学习小结】 HashSet 核心要点:
- 底层依赖 HashMap,元素作为 key,PRESENT 作为固定 value
- 所有操作都委托给 HashMap
- 去重依赖 hashCode 和 equals
- 自定义对象作为元素必须重写 hashCode 和 equals
- 选型:HashSet(无序)→ LinkedHashSet(插入顺序)→ TreeSet(排序)