在探討 HashMap 時,我們已了解「Hash」代表散列、「Map」代表映射。那麼 Set 又是什麼?從詞源上看,Set 對應數學中的「集合」概念——元素不可重複。因此 HashSet 的本質就是:以散列表(Hash Table)為底層結構,維護一個無重複元素的集合。
public class HashSet<E> extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
HashSet 繼承 AbstractSet,實現 Set、Cloneable 與 Serializable 接口。其核心存儲容器是一個 transient HashMap<E, Object> map,鍵存放集合元素,值統一使用一個靜態常量:
private static final Object PRESENT = new Object();
這個佔位對象 PRESENT 是實現「鍵唯一、值無意義」的關鍵。
public HashSet() { map = new HashMap<>(); }
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int)(c.size()/.75f) + 1, 16));
addAll(c);
}
初始容量取 集合大小/0.75 + 1 與 16 的較大值,避免頻繁擴容,兼顧空間效率。
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
LinkedHashSet 使用)HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
第三個參數 dummy 僅為區分重載,實際未使用;底層改用 LinkedHashMap 以保持插入順序。
| 方法 | 實現邏輯 | 關鍵點 |
|---|---|---|
iterator() | map.keySet().iterator() | 遍歷鍵集合 |
size() | map.size() | 委託 Map |
isEmpty() | map.isEmpty() | 委託 Map |
contains(o) | map.containsKey(o) | 判斷鍵是否存在 |
add(e) | map.put(e, PRESENT) == null | 首次放入返回 null,表示添加成功 |
remove(o) | map.remove(o) == PRESENT | 刪除鍵並比對值是否為佔位對象 |
clear() | map.clear() | 清空底層 Map |
public Object clone() {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone(); // 僅克隆 Map 結構,元素共享
return newSet;
}
僅序列化 鍵(元素),值 PRESENT 固定不需存儲。反序列化時根據 instanceof LinkedHashSet 決定重建 HashMap 或 LinkedHashMap。
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<>(map, 0, -1, 0, 0);
}
Spliterator(可分割迭代器)支援並行遍歷,將元素分批交由多線程處理,提升大數據集吞吐量。
HashSet 實際上是對 HashMap 的一層薄封裝:將元素作為 Key,固定對象作為 Value,從而以「映射」實現「集合」的數學語義。理解這一點,便能貫通其所有操作的本質。