WeakHashMap 結合 WeakReference 與 HashMap,實現 Key 可被 GC 自動回收的 Map,極適合緩存場景。
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) throw new IllegalArgumentException(...);
int capacity = 1;
while (capacity < initialCapacity) capacity <<= 1;
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
}
與 HashMap 類似,計算 2 的冪次容量,創建 Entry 數組。
public V put(K key, V value) {
Object k = maskNull(key); // 1. 包裝 null key
int h = hash(k);
Entry<K,V>[] tab = getTable(); // 2. 關鍵:調用 getTable() 清理過期條目
int i = indexFor(h, tab.length);
for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
if (h == e.hash && eq(k, e.get())) { // 3. 使用 eq() 比較,e.get() 獲取弱引用指向的對象
V oldValue = e.value;
if (value != oldValue) e.value = value;
return oldValue;
}
}
modCount++;
Entry<K,V> e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e); // 4. 新 Entry 關聯 ReferenceQueue
if (++size >= threshold) resize(tab.length * 2);
return null;
}
private Entry<K,V>[] getTable() {
expungeStaleEntries(); // 每次操作前清理
return table;
}
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i], p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e) table[i] = next;
else prev.next = next;
e.value = null; // 幫助 GC
size--;
break;
}
prev = p; p = next;
}
}
}
}
流程: 遍歷 ReferenceQueue 中已失效的弱引用,定位哈希桶,從鏈表移除,置空 value,size--。

private static class Entry<K,V> extends WeakReference<Object>> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) {
super(key, queue); // 關鍵:傳入 queue,Key 被 GC 後自動入隊
this.value = value;
this.hash = hash;
this.next = next;
}
// getKey() 返回 get() 獲取的強引用對象
}
均先調用 getTable() 觸發清理,邏輯與 HashMap 一致。
注意:WeakHashMap 的 Key 必須正確實現 equals/hashCode,且不應為長期強引用對象,否則失去自動回收意義。