面试知识库
进阶

Set与Map实现类对比#

一句话答案#

Set 本质是只用 key 的 Map:HashSet 底层是 HashMap(无序、O(1))、LinkedHashSet 加双向链表(保插入序)、TreeSet 底层是 TreeMap(红黑树排序、O(logn))。Map 三兄弟同理:HashMap 无序、LinkedHashMap 保序(可做 LRU)、TreeMap 红黑树有序。

核心要点

一、HashSet 的本质:就是一个 HashMap

public boolean add(E e) {
    return map.put(e, PRESENT) == null;   // value 是固定的占位 Object PRESENT
}
java
  • 元素存为 HashMap 的 key,value 是同一个占位对象 PRESENT
  • 去重依赖 hashCode() + equals(),所以自定义对象入 Set 必须重写这两个方法

二、Set 三实现对比

实现底层顺序时间复杂度排序依赖
HashSetHashMap无序O(1)hashCode+equals
LinkedHashSetLinkedHashMap插入顺序O(1)hashCode+equals
TreeSetTreeMap排序O(logn)Comparable/Comparator

三、Map 三实现对比

实现底层结构顺序性能null key
HashMap数组+链表+红黑树无序O(1)允许 1 个
LinkedHashMapHashMap + 双向链表插入序 / 访问序O(1)允许
TreeMap红黑树key 排序O(logn)不允许(需比较)

四、LinkedHashMap 与 LRU

  • 维护一条贯穿所有 entry 的双向链表记录顺序。
  • 构造参数 accessOrder=true 时按访问顺序排列(最近访问的移到尾部)。
  • 重写 removeEldestEntry 即可实现 LRU 缓存
new LinkedHashMap<>(16, 0.75f, true) {
    protected boolean removeEldestEntry(Map.Entry e) { return size() > CAP; }
};
java

五、TreeMap 要点

  • 基于红黑树,key 必须可比较(实现 Comparable 或传 Comparator)。
  • 支持范围查询:firstKey/lastKey/floorKey/ceilingKey/subMap/headMap/tailMap
  • key 为 null 会抛 NPE(除非自定义 Comparator 允许)。
面试回答(2分钟版)

Set 和 Map 的实现类是一一对应的,因为 Set 本质上就是只用 key 不用 value 的 Map。HashSet 底层直接就是一个 HashMap,add 元素其实是把元素当 key 放进去,value 是一个固定的占位对象 PRESENT,所以 HashSet 去重完全依赖 hashCode 和 equals,自定义对象放进 Set 一定要重写这两个方法。三种 Set:HashSet 无序、查询 O(1);LinkedHashSet 底层是 LinkedHashMap,用一条双向链表维护插入顺序;TreeSet 底层是 TreeMap,基于红黑树排序,操作 O(logn),元素必须可比较。Map 也是对应的三兄弟:HashMap 是数组加链表加红黑树、无序、允许一个 null key;LinkedHashMap 在 HashMap 基础上加双向链表,默认保持插入顺序,如果构造时把 accessOrder 设成 true 就变成访问顺序,再重写 removeEldestEntry 就能实现 LRU 缓存,这是面试常考点;TreeMap 基于红黑树,key 有序,支持 floorKey、ceilingKey、subMap 这些范围查询,但 key 不能为 null 因为要比较。选型上:要快用 Hash 系,要保顺序用 Linked 系,要排序或范围查询用 Tree 系。

追问与易错

追问方向:

  • “HashSet 怎么保证元素不重复?”→ 底层 HashMap put,依赖 key 的 hashCode 定位桶 + equals 比较,相同则不插入
  • “怎么用 LinkedHashMap 实现 LRU?”→ 构造时 accessOrder=true 按访问顺序,重写 removeEldestEntry 返回 size>容量 时自动淘汰最久未访问的头节点
  • “TreeMap 的 key 能为 null 吗?”→ 默认不能,插入时要调 compareTo 会 NPE;自定义 Comparator 处理 null 才行
  • “TreeSet 自定义对象排序怎么做?”→ 对象实现 Comparable,或构造 TreeSet 时传入 Comparator
  • “LinkedHashMap 比 HashMap 多了什么开销?”→ 每个节点多两个指针维护双向链表,内存略高,但迭代顺序可预测

易错点:

  • ❌ “HashSet 和 HashMap 没关系”——HashSet 内部就是 HashMap
  • ❌ “TreeSet 去重靠 equals”——靠比较器返回 0(见 Comparable与Comparator
  • ❌ “LinkedHashMap 只能插入序”——accessOrder=true 可切访问序,这是 LRU 基础