HashSet 源碼級淺析:哈希去重、無序性驗證與性能特徵
核心特性速覽
| 特性 | 說明 |
| 底層結構 | HashMap,value 統一 PRESENT 常量 |
| 去重機制 | hashCode() 定桶 → equals() 精確比較 |
| 順序性 | 無序,迭代順序不保證,隨擴容變化 |
| Null 支援 | 允許 1 個 null 元素 |
| 線程安全 | 非線程安全,併發用 Collections.synchronizedSet 或 ConcurrentHashMap.newKeySet() |
| 序列化/克隆 | 實現 Serializable、Cloneable |
關鍵源碼片段
public class HashSet extends AbstractSet implements Set, Cloneable, Serializable {
private transient HashMap map;
private static final Object PRESENT = new Object();
public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT) == null; }
public boolean contains(Object o) { return map.containsKey(o); }
public boolean remove(Object o) { return map.remove(o) == PRESENT; }
public Iterator iterator() { return map.keySet().iterator(); }
// ...
}
實戰驗證:去重與無序
1. 元素不可重複
Set
2. 元素無順序(十萬元素驗證)
Set set = new HashSet<>();
for (int i = 1; i <= 100000; i++) set.add(i);
int disorderCount = 0;
Integer prev = null;
for (Integer curr : set) {
if (prev != null && curr < prev) disorderCount++;
prev = curr;
}
System.out.println("無序確認: " + (disorderCount > 0) + ", 逆序對數: " + disorderCount);
// 輸出:無序確認: true, 逆序對數: 17232 (多次運行穩定)
結論:HashSet 迭代順序由哈希桶分佈決定,非插入順序,非自然順序,不可依賴順序。
性能特徵分析
| 操作 | 平均時間 | 最壞情況 | 說明 |
| add/contains/remove | O(1) | O(n) | 哈希衝突退化為鏈表/紅黑樹 |
| 遍歷 | O(n) | O(n) | 遍歷 HashMap Entry 數組 + 鏈表 |
擴容機制
- 默認負載因子 0.75,閾值 = capacity * 0.75
- 擴容為 2 倍(冪次方),重新計算所有元素 hash 分桶
- 大數據量預估初始容量:
new HashSet<>(expectedSize / 0.75f + 1)
常見誤區與最佳實踐
- 存放可變對象:加入 Set 後修改字段導致 hashCode 改變 → 無法查找/刪除 → 不可變對象或不修改影響 hash/equals 的字段
- 未重寫 hashCode/equals:默認 Object 身份比較 → 邏輯相等對象被視為不同 → 值類型必須重寫
- 併發環境直接使用:導致數據不一致、死循環 → 用 ConcurrentHashMap.newKeySet() 或 CopyOnWriteArraySet
- 依賴迭代順序:業務邏輯假設插入順序 → 改用 LinkedHashSet 保持插入順序
對比:HashSet vs LinkedHashSet vs TreeSet
| 特性 | HashSet | LinkedHashSet | TreeSet |
| 底層 | HashMap | LinkedHashMap | TreeMap (紅黑樹) |
| 順序 | 無序 | 插入順序 | 自然順序/Comparator |
| 性能 | 最快 O(1) | 略慢 O(1) | O(log n) |
| Null | 允許 1 個 | 允許 1 個 | 不允許 (NPE) |
總結
HashSet 以極致的 O(1) 增刪查換取順序不可預期。理解其HashMap 委託實現、hashCode/equals 雙重去重、負載因子擴容機制,能幫助你在去重、標籤系統、ID 集合等高頻場景做正確選型與調優。
- 上一篇文章: JDBC 連接 MySQL 實現增刪改查:MVC 分層架構完整實戰
- 下一篇文章: LinkedList 源碼級淺析:雙向鏈表、雙端隊列與迭代器特性全解析
Copyright ©
程式碼之家 All Rights Reserved