系列文章:
前言
WeakHashMap
類似HashMap
是一個基于哈希實現的無序散列表,存儲內容是鍵值對(key-value
),是非線程安全的,但是WeakHashMap
的鍵是弱鍵。
在WeakHashMap
中,當某個鍵不再正常使用時,將會被自動移除。更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的回收,這就使該鍵成為可終止的,然后被回收。回收某個鍵時,對應的鍵值對會從映射中有效地移除。因此,該類的行為與其他的 Map 實現有所不同。其定義如下:
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
可以看到HashMap
繼承AbstractMap
,實現了Map接口。
那么WeakHashMap
是怎么實現這個弱鍵的呢?WeakHashMap
是通過WeakReference
和ReferenceQueue
實現的,WeakHashMap
的key
是WeakReference
類型的,將WeakReference
和ReferenceQueue
關聯,ReferenceQueue
便會保存被GC
回收的“弱鍵”,如下(具體關于Java的四種引用類型見前文Java數據類型):
與
HashMap
類似,WeakHashMap
是通過數組table
保存Entry
(鍵值對),而每一個Entry
是單向鏈表,Entry
類繼承WeakReference
,新建Entry
時,便會把Entry
的key
和ReferceQueue
關聯當“弱鍵”不再被其它對象引用,便會被GC回收時。GC回收該“弱鍵”時,這個“弱鍵”也同時會被添加到
ReferenceQueue(queue)
隊列中當再次操作
WeakHashMap
時,如獲取size
大小,擴容等,會先同步table和queue
,table
中保存了全部的鍵值對,而queue
中保存被GC回收的鍵值對,刪除table
中被GC回收的鍵值對。
和HashMap
一樣,WeakHashMap
是不同步的。可以使用 Collections.synchronizedMap
方法來構造同步的 WeakHashMap
。
本文源碼分析基于jdk 1.8.0_121
繼承關系
WeakHashMap繼承關系
java.lang.Object
|___ java.util.AbstractMap<K,V>
|___ java.util.WeakHashMap<K,V>
所有已實現的接口:
Map<K,V>
關系圖
-
table
是Entry[]
型的數組,而Entry
其實是個單向鏈表,所以WeakHashMap
的底層實現是由數組和鏈表實現的,此處的Entry
和HashMap
中的Node
類似,但是Entry
繼承自WeakReference<Object>
-
size
是WeakHashMap
的實際大小 -
threshold
為是否需要調整WeakHashMap
的容量的閾值,等于加載因子乘以容量,當size
達到threshold
時,便對WeakHashMap
擴容 -
loadFactor
是加載因子 -
modCount
是修改WeakHashMap
結構的計數值,用來實現fail-fast
機制 -
queue
用來保存被GC清除的弱引用的鍵
構造函數
// 帶初始容量和加載因子的構造函數
public WeakHashMap(int initialCapacity, float loadFactor)
// 帶初始容量的構造函數
public WeakHashMap(int initialCapacity)
// 默認構造函數
public WeakHashMap()
// 包含子Map的構造函數
public WeakHashMap(Map<? extends K, ? extends V> m)
API
void clear()
Object clone()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Entry<K, V>> entrySet()
V get(Object key)
boolean isEmpty()
Set<K> keySet()
V put(K key, V value)
void putAll(Map<? extends K, ? extends V> m)
V remove(Object key)
int size()
Collection<V> values()
源碼分析
成員變量
// 默認的初始容量是16,必須是2的冪。
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// 最大容量,必須小于2的30次方,初始容量過大將被這個值代替
private static final int MAXIMUM_CAPACITY = 1 << 30;
// 默認加載因子
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 存儲數據的Entry[]數組,長度必須是2的冪
// WeakHashMap是采用拉鏈法實現的,每一個Entry本質上是一個單向鏈表
Entry<K,V>[] table;
// WeakHashMap的大小,它是WeakHashMap保存的鍵值對的數量
private int size;
// 閾值用于判斷是否需要調整WeakHashMap的容量(threshold=容量*加載因子)
private int threshold;
// 加載因子實際大小
private final float loadFactor;
// HashMap被改變的次數
private transient volatile int modCount;
// 弱引用和ReferenceQueue 聯合使用:如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
構造函數
// 指定“初始容量”和“加載因子”的構造函數
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
// 找出大于initialCapacity的最小的2的冪
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
// 創建Entry數組
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
}
// 指定初始容量的構造函數
public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
// 默認構造函數
public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
// 包含“子Map”的構造函數
public WeakHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(m);
}
定位桶位置
// 與操作 hash值和length-1與操作得到索引值
private static int indexFor(int h, int length) {
return h & (length-1);
}
增加元素
// key-value鍵值放入WeakHashMap中
public V put(K key, V value) {
// 如果key是null則用NULL_KEY代替
// private static final Object NULL_KEY = new Object();
Object k = maskNull(key);
int h = hash(k);
// 移除內部不用的條目并返回table
Entry<K,V>[] tab = getTable();
// 找到索引值
int i = indexFor(h, tab.length);
for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
// 若“該key”對應的鍵值對已經存在,則用新的value取代舊的value
if (h == e.hash && eq(k, e.get())) {
V oldValue = e.value;
if (value != oldValue)
e.value = value;
return oldValue;
}
}
// 如果key不在WeakHashMap中,則將key-value添加到HashMap中
modCount++;
Entry<K,V> e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e);
if (++size >= threshold)
resize(tab.length * 2);
return null;
}
// 將m的元素全部放入WeakHashMap中
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;
// 如果當前容量小于目標容量,則進行擴容
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
// 逐個添加元素到WeakHashMap中
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
獲取元素
// 獲取key對應的value
public V get(Object key) {
Object k = maskNull(key);
// 獲取key的哈希值
int h = hash(k);
Entry<K,V>[] tab = getTable();
int index = indexFor(h, tab.length);
Entry<K,V> e = tab[index];
// 在該哈希值索引對應鏈表上尋找鍵值等于key的節點的值
while (e != null) {
if (e.hash == h && eq(k, e.get()))
return e.value;
e = e.next;
}
return null;
}
// 返回鍵值為key的鍵值對
Entry<K,V> getEntry(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int index = indexFor(h, tab.length);
Entry<K,V> e = tab[index];
while (e != null && !(e.hash == h && eq(k, e.get())))
e = e.next;
return e;
}
刪除元素
// 刪除key對應的鍵值對
public V remove(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int i = indexFor(h, tab.length);
Entry<K,V> prev = tab[i];
Entry<K,V> e = prev;
// 刪除單向鏈表中符合條件的節點
while (e != null) {
Entry<K,V> next = e.next;
if (h == e.hash && eq(k, e.get())) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return e.value;
}
prev = e;
e = next;
}
return null;
}
resize
// 重新調整WeakHashMap的大小
void resize(int newCapacity) {
Entry<K,V>[] oldTable = getTable();
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
// 新建一個newTable,舊表的全部元素都添加到其中
Entry<K,V>[] newTable = newTable(newCapacity);
transfer(oldTable, newTable);
table = newTable;
if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}
// WeakHashMap中的全部元素都添加到newTable中
private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
for (int j = 0; j < src.length; ++j) {
Entry<K,V> e = src[j];
src[j] = null;
while (e != null) {
Entry<K,V> next = e.next;
Object key = e.get();
// key為null,則置
if (key == null) {
e.next = null; // Help GC
e.value = null; // " "
size--;
} else {
// 找到e的hash在新數組下對應的索引值
int i = indexFor(e.hash, dest.length);
e.next = dest[i];
dest[i] = e;
}
e = next;
}
}
}
清空無用鍵值對
// 清空WeakHashMap中無用的鍵值對
// 因為當WeakHashMap中某個鍵值對沒有引用時,會被GC自動回收,被回收的弱引用key也會被添加到queue中
// 遍歷queue中所有key,在table中對應刪除該key對應的鍵值對
private void expungeStaleEntries() {
// 遍歷queue中所有key
for (Object x; (x = queue.poll()) != null; ) {
// 加同步鎖
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> 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; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
Entry數據結構
// 單向鏈表,繼承自WeakReference<Object>
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next; // 下一個節點
// 構造函數,每構造一個Entry都與queue綁定
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
// 判斷兩個Entry是否相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
// 實現hashcode
public int hashCode() {
K k = getKey();
V v = getValue();
return Objects.hashCode(k) ^ Objects.hashCode(v);
}
public String toString() {
return getKey() + "=" + getValue();
}
}
遍歷
假設key和value
都是String
型
- 根據
entrySet()
通過Iterator
遍歷
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
key = (String)entry.getKey();
value = (String)entry.getValue();
}
- 根據
keySet()
通過Iterator
遍歷
Iterator iter = map.keySet().iterator();
while(iter.hasNext()){
key = (String)iter.next();
value = (String)map.get(key);
}
- 根據
value()
通過Iterator
遍歷
Iterator iter = map.values().iterator();
while(iter.hasNext()){
value = (String)iter.next;
}
可以發現,遍歷WeakHashMap
的方法和遍歷HashMap
的方法完全一致。