transient Node<K,V>[] table; // 桶數組,長度為 2 的冪次
transient int size; // 鍵值對數量
int threshold; // 擴容閾值 = capacity * loadFactor
final float loadFactor; // 負載因子,默認 0.75f
transient int modCount; // 結構修改次數,用於 fail-fast
Node 為單向鏈表節點(JDK 8+ 高桶轉紅黑樹):int hash; K key; V value; Node<K,V> next;
public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); }
public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) throw new IllegalArgumentException();
if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException();
this.loadFactor = loadFactor;
this.threshold = initialCapacity; // 延遲初始化,首次 put 時才創建 table
}
table == EMPTY_TABLE → inflateTable(threshold) 創建桶數組。putForNullKey(value),固定放入 table[0]。hash(key) → key.hashCode() ^ (h >>> 16)(高位混合,減少碰撞)。index = (n - 1) & hash(n 為 2 的冪次,等價取模但更快)。hash 與 key.equals(k),找到則覆蓋舊值並返回舊值。addEntry(hash, key, value, index) → 頭插法(JDK 7)或尾插法(JDK 8+)。if (++size > threshold) resize(newCapacity)。void resize(int newCapacity) {
Node[] oldTable = table;
if (oldTable.length == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; }
Node[] newTable = new Node[newCapacity];
transfer(newTable); // 重新計算每個節點在新桶的位置並遷移
table = newTable;
threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
關鍵點:擴容為 2 倍,重新哈希分佈,JDK 8+ 利用 hash & oldCap 判斷節點留在原桶還是遷移到 oldIndex + oldCap,避免全量重新計算 hash。
getForNullKey() 直接取 table[0] 鏈表首節點。e.hash == hash && (e.key == key || key.equals(e.key)) 命中返回 e.value。null。時間複雜度:理想情況 O(1),極端碰撞 O(n)(JDK 8+ 樹化後 O(log n))。
class User {
private int idNumber;
public User(int id) { this.idNumber = id; }
@Override public int hashCode() { return idNumber; }
@Override public boolean equals(Object obj) {
return obj instanceof User && idNumber == ((User) obj).idNumber;
}
}
// 測試
Map<User, String> map = new HashMap<>();
map.put(new User(3), "張三");
System.out.println(map.get(new User(3))); // 張三(同 hashCode + equals 視為同一 key)
鐵律:equals() 為 true 的兩個對象,hashCode() 必須相等。違反會導致「能 put 進去,卻 get 不出來」。
x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == falsenew HashMap<>(expectedSize),避免多次擴容。ConcurrentHashMap,HashMap 非線程安全。