概要
這一章,我們對WeakHashMap進行學習。
我們先對WeakHashMap有個整體認識,然后再學習它的源碼,最后再通過實例來學會使用WeakHashMap。
第1部分 WeakHashMap介紹
第2部分 WeakHashMap數據結構
第3部分 WeakHashMap源碼解析(基于JDK1.6.0_45)
第4部分 WeakHashMap遍歷方式
第5部分 WeakHashMap示例
## 第1部分 WeakHashMap介紹
WeakHashMap簡介
? WeakHashMap 繼承于AbstractMap,實現了Map接口。
? ? 和HashMap一樣,WeakHashMap 也是一個散列表,它存儲的內容也是鍵值對(key-value)映射,而且鍵和值都可以是null。
? 不過WeakHashMap的鍵是“弱鍵”。在 WeakHashMap 中,當某個鍵不再正常使用時,會被從WeakHashMap中被自動移除。更精確地說,對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。某個鍵被終止時,它對應的鍵值對也就從映射中有效地移除了。
? ? 這個“弱鍵”的原理呢?大致上就是,通過WeakReference和ReferenceQueue實現的。 WeakHashMap的key是“弱鍵”,即是WeakReference類型的;ReferenceQueue是一個隊列,它會保存被GC回收的“弱鍵”。實現步驟是:
? ? (01) 新建WeakHashMap,將“鍵值對”添加到WeakHashMap中。
? ? ? ? ? 實際上,WeakHashMap是通過數組table保存Entry(鍵值對);每一個Entry實際上是一個單向鏈表,即Entry是鍵值對鏈表。
? (02) 當某“弱鍵”不再被其它對象引用,并被GC回收時。在GC回收該“弱鍵”時,這個“弱鍵”也同時會被添加到ReferenceQueue(queue)隊列中。
? (03) 當下一次我們需要操作WeakHashMap時,會先同步table和queue。table中保存了全部的鍵值對,而queue中保存被GC回收的鍵值對;同步它們,就是刪除table中被GC回收的鍵值對。
? 這就是“弱鍵”如何被自動從WeakHashMap中刪除的步驟了。
和HashMap一樣,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法來構造同步的 WeakHashMap。
WeakHashMap的構造函數
WeakHashMap共有4個構造函數,如下:
```
// 默認構造函數。
WeakHashMap()
// 指定“容量大小”的構造函數
WeakHashMap(int capacity)
// 指定“容量大小”和“加載因子”的構造函數
WeakHashMap(int capacity, float loadFactor)
// 包含“子Map”的構造函數
WeakHashMap(Map<? extends K, ? extends V> map)
```
WeakHashMap的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> map)
V? ? ? ? ? ? ? ? ? ? ? remove(Object key)
int? ? ? ? ? ? ? ? ? ? size()
Collection<V>? ? ? ? ? values()
```
## 第2部分 WeakHashMap數據結構
WeakHashMap的繼承關系如下
```
java.lang.Object
? ?? ? java.util.AbstractMap<K, V>
? ? ? ? ?? ? java.util.WeakHashMap<K, V>
public class WeakHashMap<K,V>
? ? extends AbstractMap<K,V>
? ? implements Map<K,V> {}
```
WeakHashMap與Map關系如下圖:
從圖中可以看出:
(01) WeakHashMap繼承于AbstractMap,并且實現了Map接口。
(02) WeakHashMap是哈希表,但是它的鍵是"弱鍵"。WeakHashMap中保護幾個重要的成員變量:table, size, threshold, loadFactor, modCount, queue。
table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。
size是Hashtable的大小,它是Hashtable保存的鍵值對的數量。
threshold是Hashtable的閾值,用于判斷是否需要調整Hashtable的容量。threshold的值="容量*加載因子"。
loadFactor就是加載因子。
modCount是用來實現fail-fast機制的
queue保存的是“已被GC清除”的“弱引用的鍵”。
## 第3部分 WeakHashMap源碼解析
下面對WeakHashMap的源碼進行說明
```
package java.util;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
public class WeakHashMap<K,V>
? ? extends AbstractMap<K,V>
? ? implements Map<K,V> {
? ? // 默認的初始容量是16,必須是2的冪。
? ? private static final int DEFAULT_INITIAL_CAPACITY = 16;
? ? // 最大容量(必須是2的冪且小于2的30次方,傳入容量過大將被這個值替換)
? ? private static final int MAXIMUM_CAPACITY = 1 << 30;
? ? // 默認加載因子
? ? private static final float DEFAULT_LOAD_FACTOR = 0.75f;
? ? // 存儲數據的Entry數組,長度是2的冪。
? ? // WeakHashMap是采用拉鏈法實現的,每一個Entry本質上是一個單向鏈表
? ? private Entry[] table;
? ? // WeakHashMap的大小,它是WeakHashMap保存的鍵值對的數量
? ? private int size;
? ? // WeakHashMap的閾值,用于判斷是否需要調整WeakHashMap的容量(threshold = 容量*加載因子)
? ? private int threshold;
? ? // 加載因子實際大小
? ? private final float loadFactor;
? ? // queue保存的是“已被GC清除”的“弱引用的鍵”。
? ? // 弱引用和ReferenceQueue 是聯合使用的:如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中
? ? private final ReferenceQueue<K> queue = new ReferenceQueue<K>();
? ? // WeakHashMap被改變的次數
? ? private volatile int modCount;
? ? // 指定“容量大小”和“加載因子”的構造函數
? ? public WeakHashMap(int initialCapacity, float loadFactor) {
? ? ? ? if (initialCapacity < 0)
? ? ? ? ? ? throw new IllegalArgumentException("Illegal Initial Capacity: "+
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initialCapacity);
? ? ? ? // WeakHashMap的最大容量只能是MAXIMUM_CAPACITY
? ? ? ? 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;
? ? ? ? // 創(chuàng)建Entry數組,用來保存數據
? ? ? ? table = new Entry[capacity];
? ? ? ? // 設置“加載因子”
? ? ? ? this.loadFactor = loadFactor;
? ? ? ? // 設置“WeakHashMap閾值”,當WeakHashMap中存儲數據的數量達到threshold時,就需要將WeakHashMap的容量加倍。
? ? ? ? threshold = (int)(capacity * loadFactor);
? ? }
? ? // 指定“容量大小”的構造函數
? ? public WeakHashMap(int initialCapacity) {
? ? ? ? this(initialCapacity, DEFAULT_LOAD_FACTOR);
? ? }
? ? // 默認構造函數。
? ? public WeakHashMap() {
? ? ? ? this.loadFactor = DEFAULT_LOAD_FACTOR;
? ? ? ? threshold = (int)(DEFAULT_INITIAL_CAPACITY);
? ? ? ? table = new Entry[DEFAULT_INITIAL_CAPACITY];
? ? }
? ? // 包含“子Map”的構造函數
? ? public WeakHashMap(Map<? extends K, ? extends V> m) {
? ? ? ? this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, 16),
? ? ? ? ? ? DEFAULT_LOAD_FACTOR);
? ? ? ? // 將m中的全部元素逐個添加到WeakHashMap中
? ? ? ? putAll(m);
? ? }
? ? // 鍵為null的mask值。
? ? // 因為WeakReference中允許“null的key”,若直接插入“null的key”,將其當作弱引用時,會被刪除。
? ? // 因此,這里對于“key為null”的清空,都統一替換為“key為NULL_KEY”,“NULL_KEY”是“靜態(tài)的final常量”。
? ? private static final Object NULL_KEY = new Object();
? ? // 對“null的key”進行特殊處理
? ? private static Object maskNull(Object key) {
? ? ? ? return (key == null ? NULL_KEY : key);
? ? }
? ? // 還原對“null的key”的特殊處理
? ? private static <K> K unmaskNull(Object key) {
? ? ? ? return (K) (key == NULL_KEY ? null : key);
? ? }
? ? // 判斷“x”和“y”是否相等
? ? static boolean eq(Object x, Object y) {
? ? ? ? return x == y || x.equals(y);
? ? }
? ? // 返回索引值
? ? // h & (length-1)保證返回值的小于length
? ? static int indexFor(int h, int length) {
? ? ? ? return h & (length-1);
? ? }
? ? // 清空table中無用鍵值對。原理如下:
? ? // (01) 當WeakHashMap中某個“弱引用的key”由于沒有再被引用而被GC收回時,
? ? //? 被回收的“該弱引用key”也被會被添加到"ReferenceQueue(queue)"中。
? ? // (02) 當我們執(zhí)行expungeStaleEntries時,
? ? //? 就遍歷"ReferenceQueue(queue)"中的所有key
? ? //? 然后就在“WeakReference的table”中刪除與“ReferenceQueue(queue)中key”對應的鍵值對
? ? private void expungeStaleEntries() {
? ? ? ? Entry<K,V> e;
? ? ? ? while ( (e = (Entry<K,V>) queue.poll()) != null) {
? ? ? ? ? ? int h = e.hash;
? ? ? ? ? ? int i = indexFor(h, 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.next = null;? // Help GC
? ? ? ? ? ? ? ? ? ? e.value = null; //? "? "
? ? ? ? ? ? ? ? ? ? size--;
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? prev = p;
? ? ? ? ? ? ? ? p = next;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? // 獲取WeakHashMap的table(存放鍵值對的數組)
? ? private Entry[] getTable() {
? ? ? ? // 刪除table中“已被GC回收的key對應的鍵值對”
? ? ? ? expungeStaleEntries();
? ? ? ? return table;
? ? }
? ? // 獲取WeakHashMap的實際大小
? ? public int size() {
? ? ? ? if (size == 0)
? ? ? ? ? ? return 0;
? ? ? ? // 刪除table中“已被GC回收的key對應的鍵值對”
? ? ? ? expungeStaleEntries();
? ? ? ? return size;
? ? }
? ? public boolean isEmpty() {
? ? ? ? return size() == 0;
? ? }
? ? // 獲取key對應的value
? ? public V get(Object key) {
? ? ? ? Object k = maskNull(key);
? ? ? ? // 獲取key的hash值。
? ? ? ? int h = HashMap.hash(k.hashCode());
? ? ? ? Entry[] tab = getTable();
? ? ? ? int index = indexFor(h, tab.length);
? ? ? ? Entry<K,V> e = tab[index];
? ? ? ? // 在“該hash值對應的鏈表”上查找“鍵值等于key”的元素
? ? ? ? while (e != null) {
? ? ? ? ? ? if (e.hash == h && eq(k, e.get()))
? ? ? ? ? ? ? ? return e.value;
? ? ? ? ? ? e = e.next;
? ? ? ? }
? ? ? ? return null;
? ? }
? ? // WeakHashMap是否包含key
? ? public boolean containsKey(Object key) {
? ? ? ? return getEntry(key) != null;
? ? }
? ? // 返回“鍵為key”的鍵值對
? ? Entry<K,V> getEntry(Object key) {
? ? ? ? Object k = maskNull(key);
? ? ? ? int h = HashMap.hash(k.hashCode());
? ? ? ? Entry[] 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-value”添加到WeakHashMap中
? ? public V put(K key, V value) {
? ? ? ? K k = (K) maskNull(key);
? ? ? ? int h = HashMap.hash(k.hashCode());
? ? ? ? Entry[] 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”添加到table中
? ? ? ? modCount++;
? ? ? ? Entry<K,V> e = tab[i];
? ? ? ? tab[i] = new Entry<K,V>(k, value, queue, h, e);
? ? ? ? if (++size >= threshold)
? ? ? ? ? ? resize(tab.length * 2);
? ? ? ? return null;
? ? }
? ? // 重新調整WeakHashMap的大小,newCapacity是調整后的單位
? ? void resize(int newCapacity) {
? ? ? ? Entry[] oldTable = getTable();
? ? ? ? int oldCapacity = oldTable.length;
? ? ? ? if (oldCapacity == MAXIMUM_CAPACITY) {
? ? ? ? ? ? threshold = Integer.MAX_VALUE;
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? // 新建一個newTable,將“舊的table”的全部元素添加到“新的newTable”中,
? ? ? ? // 然后,將“新的newTable”賦值給“舊的table”。
? ? ? ? Entry[] newTable = new Entry[newCapacity];
? ? ? ? transfer(oldTable, newTable);
? ? ? ? table = newTable;
? ? ? ? if (size >= threshold / 2) {
? ? ? ? ? ? threshold = (int)(newCapacity * loadFactor);
? ? ? ? } else {
? ? ? ? ? ? // 刪除table中“已被GC回收的key對應的鍵值對”
? ? ? ? ? ? expungeStaleEntries();
? ? ? ? ? ? transfer(newTable, oldTable);
? ? ? ? ? ? table = oldTable;
? ? ? ? }
? ? }
? ? // 將WeakHashMap中的全部元素都添加到newTable中
? ? private void transfer(Entry[] src, Entry[] 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();
? ? ? ? ? ? ? ? if (key == null) {
? ? ? ? ? ? ? ? ? ? e.next = null;? // Help GC
? ? ? ? ? ? ? ? ? ? e.value = null; //? "? "
? ? ? ? ? ? ? ? ? ? size--;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? int i = indexFor(e.hash, dest.length);
? ? ? ? ? ? ? ? ? ? e.next = dest[i];
? ? ? ? ? ? ? ? ? ? dest[i] = e;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? e = next;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? // 將"m"的全部元素都添加到WeakHashMap中
? ? public void putAll(Map<? extends K, ? extends V> m) {
? ? ? ? int numKeysToBeAdded = m.size();
? ? ? ? if (numKeysToBeAdded == 0)
? ? ? ? ? ? return;
? ? ? ? // 計算容量是否足夠,
? ? ? ? // 若“當前實際容量 < 需要的容量”,則將容量x2。
? ? ? ? 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);
? ? ? ? }
? ? ? ? // 將“m”中的元素逐個添加到WeakHashMap中。
? ? ? ? for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
? ? ? ? ? ? put(e.getKey(), e.getValue());
? ? }
? ? // 刪除“鍵為key”元素
? ? public V remove(Object key) {
? ? ? ? Object k = maskNull(key);
? ? ? ? // 獲取哈希值。
? ? ? ? int h = HashMap.hash(k.hashCode());
? ? ? ? Entry[] tab = getTable();
? ? ? ? int i = indexFor(h, tab.length);
? ? ? ? Entry<K,V> prev = tab[i];
? ? ? ? Entry<K,V> e = prev;
? ? ? ? // 刪除鏈表中“鍵為key”的元素
? ? ? ? // 本質是“刪除單向鏈表中的節(jié)點”
? ? ? ? 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;
? ? }
? ? // 刪除“鍵值對”
? ? Entry<K,V> removeMapping(Object o) {
? ? ? ? if (!(o instanceof Map.Entry))
? ? ? ? ? ? return null;
? ? ? ? Entry[] tab = getTable();
? ? ? ? Map.Entry entry = (Map.Entry)o;
? ? ? ? Object k = maskNull(entry.getKey());
? ? ? ? int h = HashMap.hash(k.hashCode());
? ? ? ? int i = indexFor(h, tab.length);
? ? ? ? Entry<K,V> prev = tab[i];
? ? ? ? Entry<K,V> e = prev;
? ? ? ? // 刪除鏈表中的“鍵值對e”
? ? ? ? // 本質是“刪除單向鏈表中的節(jié)點”
? ? ? ? while (e != null) {
? ? ? ? ? ? Entry<K,V> next = e.next;
? ? ? ? ? ? if (h == e.hash && e.equals(entry)) {
? ? ? ? ? ? ? ? modCount++;
? ? ? ? ? ? ? ? size--;
? ? ? ? ? ? ? ? if (prev == e)
? ? ? ? ? ? ? ? ? ? tab[i] = next;
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? prev.next = next;
? ? ? ? ? ? ? ? return e;
? ? ? ? ? ? }
? ? ? ? ? ? prev = e;
? ? ? ? ? ? e = next;
? ? ? ? }
? ? ? ? return null;
? ? }
? ? // 清空WeakHashMap,將所有的元素設為null
? ? public void clear() {
? ? ? ? while (queue.poll() != null)
? ? ? ? ? ? ;
? ? ? ? modCount++;
? ? ? ? Entry[] tab = table;
? ? ? ? for (int i = 0; i < tab.length; ++i)
? ? ? ? ? ? tab[i] = null;
? ? ? ? size = 0;
? ? ? ? while (queue.poll() != null)
? ? ? ? ? ? ;
? ? }
? ? // 是否包含“值為value”的元素
? ? public boolean containsValue(Object value) {
? ? ? ? // 若“value為null”,則調用containsNullValue()查找
? ? ? ? if (value==null)
? ? ? ? ? ? return containsNullValue();
? ? ? ? // 若“value不為null”,則查找WeakHashMap中是否有值為value的節(jié)點。
? ? ? ? Entry[] tab = getTable();
? ? ? ? for (int i = tab.length ; i-- > 0 ;)
? ? ? ? ? ? for (Entry e = tab[i] ; e != null ; e = e.next)
? ? ? ? ? ? ? ? if (value.equals(e.value))
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? return false;
? ? }
? ? // 是否包含null值
? ? private boolean containsNullValue() {
? ? ? ? Entry[] tab = getTable();
? ? ? ? for (int i = tab.length ; i-- > 0 ;)
? ? ? ? ? ? for (Entry e = tab[i] ; e != null ; e = e.next)
? ? ? ? ? ? ? ? if (e.value==null)
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? return false;
? ? }
? ? // Entry是單向鏈表。
? ? // 它是 “WeakHashMap鏈式存儲法”對應的鏈表。
? ? // 它實現了Map.Entry 接口,即實現getKey(), getValue(), setValue(V value), equals(Object o), hashCode()這些函數
? ? private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
? ? ? ? private V value;
? ? ? ? private final int hash;
? ? ? ? // 指向下一個節(jié)點
? ? ? ? private Entry<K,V> next;
? ? ? ? // 構造函數。
? ? ? ? Entry(K key, V value,
? ? ? ? ? ReferenceQueue<K> queue,
? ? ? ? ? ? ? int hash, Entry<K,V> next) {
? ? ? ? ? ? super(key, queue);
? ? ? ? ? ? this.value = value;
? ? ? ? ? ? this.hash? = hash;
? ? ? ? ? ? this.next? = next;
? ? ? ? }
? ? ? ? public K getKey() {
? ? ? ? ? ? return WeakHashMap.<K>unmaskNull(get());
? ? ? ? }
? ? ? ? public V getValue() {
? ? ? ? ? ? return value;
? ? ? ? }
? ? ? ? public V setValue(V newValue) {
? ? ? ? V oldValue = value;
? ? ? ? ? ? value = newValue;
? ? ? ? ? ? return oldValue;
? ? ? ? }
? ? ? ? // 判斷兩個Entry是否相等
? ? ? ? // 若兩個Entry的“key”和“value”都相等,則返回true。
? ? ? ? // 否則,返回false
? ? ? ? public boolean equals(Object o) {
? ? ? ? ? ? if (!(o instanceof Map.Entry))
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? Map.Entry e = (Map.Entry)o;
? ? ? ? ? ? Object k1 = getKey();
? ? ? ? ? ? Object k2 = e.getKey();
? ? ? ? ? ? if (k1 == k2 || (k1 != null && k1.equals(k2))) {
? ? ? ? ? ? ? ? Object v1 = getValue();
? ? ? ? ? ? ? ? Object v2 = e.getValue();
? ? ? ? ? ? ? ? if (v1 == v2 || (v1 != null && v1.equals(v2)))
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? // 實現hashCode()
? ? ? ? public int hashCode() {
? ? ? ? ? ? Object k = getKey();
? ? ? ? ? ? Object v = getValue();
? ? ? ? ? ? return? ((k==null ? 0 : k.hashCode()) ^
? ? ? ? ? ? ? ? ? ? (v==null ? 0 : v.hashCode()));
? ? ? ? }
? ? ? ? public String toString() {
? ? ? ? ? ? return getKey() + "=" + getValue();
? ? ? ? }
? ? }
? ? // HashIterator是WeakHashMap迭代器的抽象出來的父類,實現了公共了函數。
? ? // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3個子類。
? ? private abstract class HashIterator<T> implements Iterator<T> {
? ? ? ? // 當前索引
? ? ? ? int index;
? ? ? ? // 當前元素
? ? ? ? Entry<K,V> entry = null;
? ? ? ? // 上一次返回元素
? ? ? ? Entry<K,V> lastReturned = null;
? ? ? ? // expectedModCount用于實現fast-fail機制。
? ? ? ? int expectedModCount = modCount;
? ? ? ? // 下一個鍵(強引用)
? ? ? ? Object nextKey = null;
? ? ? ? // 當前鍵(強引用)
? ? ? ? Object currentKey = null;
? ? ? ? // 構造函數
? ? ? ? HashIterator() {
? ? ? ? ? ? index = (size() != 0 ? table.length : 0);
? ? ? ? }
? ? ? ? // 是否存在下一個元素
? ? ? ? public boolean hasNext() {
? ? ? ? ? ? Entry[] t = table;
? ? ? ? ? ? // 一個Entry就是一個單向鏈表
? ? ? ? ? ? // 若該Entry的下一個節(jié)點不為空,就將next指向下一個節(jié)點;
? ? ? ? ? ? // 否則,將next指向下一個鏈表(也是下一個Entry)的不為null的節(jié)點。
? ? ? ? ? ? while (nextKey == null) {
? ? ? ? ? ? ? ? Entry<K,V> e = entry;
? ? ? ? ? ? ? ? int i = index;
? ? ? ? ? ? ? ? while (e == null && i > 0)
? ? ? ? ? ? ? ? ? ? e = t[--i];
? ? ? ? ? ? ? ? entry = e;
? ? ? ? ? ? ? ? index = i;
? ? ? ? ? ? ? ? if (e == null) {
? ? ? ? ? ? ? ? ? ? currentKey = null;
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? nextKey = e.get(); // hold on to key in strong ref
? ? ? ? ? ? ? ? if (nextKey == null)
? ? ? ? ? ? ? ? ? ? entry = entry.next;
? ? ? ? ? ? }
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? // 獲取下一個元素
? ? ? ? protected Entry<K,V> nextEntry() {
? ? ? ? ? ? if (modCount != expectedModCount)
? ? ? ? ? ? ? ? throw new ConcurrentModificationException();
? ? ? ? ? ? if (nextKey == null && !hasNext())
? ? ? ? ? ? ? ? throw new NoSuchElementException();
? ? ? ? ? ? lastReturned = entry;
? ? ? ? ? ? entry = entry.next;
? ? ? ? ? ? currentKey = nextKey;
? ? ? ? ? ? nextKey = null;
? ? ? ? ? ? return lastReturned;
? ? ? ? }
? ? ? ? // 刪除當前元素
? ? ? ? public void remove() {
? ? ? ? ? ? if (lastReturned == null)
? ? ? ? ? ? ? ? throw new IllegalStateException();
? ? ? ? ? ? if (modCount != expectedModCount)
? ? ? ? ? ? ? ? throw new ConcurrentModificationException();
? ? ? ? ? ? WeakHashMap.this.remove(currentKey);
? ? ? ? ? ? expectedModCount = modCount;
? ? ? ? ? ? lastReturned = null;
? ? ? ? ? ? currentKey = null;
? ? ? ? }
? ? }
? ? // value的迭代器
? ? private class ValueIterator extends HashIterator<V> {
? ? ? ? public V next() {
? ? ? ? ? ? return nextEntry().value;
? ? ? ? }
? ? }
? ? // key的迭代器
? ? private class KeyIterator extends HashIterator<K> {
? ? ? ? public K next() {
? ? ? ? ? ? return nextEntry().getKey();
? ? ? ? }
? ? }
? ? // Entry的迭代器
? ? private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
? ? ? ? public Map.Entry<K,V> next() {
? ? ? ? ? ? return nextEntry();
? ? ? ? }
? ? }
? ? // WeakHashMap的Entry對應的集合
? ? private transient Set<Map.Entry<K,V>> entrySet = null;
? ? // 返回“key的集合”,實際上返回一個“KeySet對象”
? ? public Set<K> keySet() {
? ? ? ? Set<K> ks = keySet;
? ? ? ? return (ks != null ? ks : (keySet = new KeySet()));
? ? }
? ? // Key對應的集合
? ? // KeySet繼承于AbstractSet,說明該集合中沒有重復的Key。
? ? private class KeySet extends AbstractSet<K> {
? ? ? ? public Iterator<K> iterator() {
? ? ? ? ? ? return new KeyIterator();
? ? ? ? }
? ? ? ? public int size() {
? ? ? ? ? ? return WeakHashMap.this.size();
? ? ? ? }
? ? ? ? public boolean contains(Object o) {
? ? ? ? ? ? return containsKey(o);
? ? ? ? }
? ? ? ? public boolean remove(Object o) {
? ? ? ? ? ? if (containsKey(o)) {
? ? ? ? ? ? ? ? WeakHashMap.this.remove(o);
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? public void clear() {
? ? ? ? ? ? WeakHashMap.this.clear();
? ? ? ? }
? ? }
? ? // 返回“value集合”,實際上返回的是一個Values對象
? ? public Collection<V> values() {
? ? ? ? Collection<V> vs = values;
? ? ? ? return (vs != null ?? vs : (values = new Values()));
? ? }
? ? // “value集合”
? ? // Values繼承于AbstractCollection,不同于“KeySet繼承于AbstractSet”,
? ? // Values中的元素能夠重復。因為不同的key可以指向相同的value。
? ? private class Values extends AbstractCollection<V> {
? ? ? ? public Iterator<V> iterator() {
? ? ? ? ? ? return new ValueIterator();
? ? ? ? }
? ? ? ? public int size() {
? ? ? ? ? ? return WeakHashMap.this.size();
? ? ? ? }
? ? ? ? public boolean contains(Object o) {
? ? ? ? ? ? return containsValue(o);
? ? ? ? }
? ? ? ? public void clear() {
? ? ? ? ? ? WeakHashMap.this.clear();
? ? ? ? }
? ? }
? ? // 返回“WeakHashMap的Entry集合”
? ? // 它實際是返回一個EntrySet對象
? ? public Set<Map.Entry<K,V>> entrySet() {
? ? ? ? Set<Map.Entry<K,V>> es = entrySet;
? ? ? ? return es != null ? es : (entrySet = new EntrySet());
? ? }
? ? // EntrySet對應的集合
? ? // EntrySet繼承于AbstractSet,說明該集合中沒有重復的EntrySet。
? ? private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
? ? ? ? public Iterator<Map.Entry<K,V>> iterator() {
? ? ? ? ? ? return new EntryIterator();
? ? ? ? }
? ? ? ? // 是否包含“值(o)”
? ? ? ? public boolean contains(Object o) {
? ? ? ? ? ? if (!(o instanceof Map.Entry))
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? Map.Entry e = (Map.Entry)o;
? ? ? ? ? ? Object k = e.getKey();
? ? ? ? ? ? Entry candidate = getEntry(e.getKey());
? ? ? ? ? ? return candidate != null && candidate.equals(e);
? ? ? ? }
? ? ? ? // 刪除“值(o)”
? ? ? ? public boolean remove(Object o) {
? ? ? ? ? ? return removeMapping(o) != null;
? ? ? ? }
? ? ? ? // 返回WeakHashMap的大小
? ? ? ? public int size() {
? ? ? ? ? ? return WeakHashMap.this.size();
? ? ? ? }
? ? ? ? // 清空WeakHashMap
? ? ? ? public void clear() {
? ? ? ? ? ? WeakHashMap.this.clear();
? ? ? ? }
? ? ? ? // 拷貝函數。將WeakHashMap中的全部元素都拷貝到List中
? ? ? ? private List<Map.Entry<K,V>> deepCopy() {
? ? ? ? ? ? List<Map.Entry<K,V>> list = new ArrayList<Map.Entry<K,V>>(size());
? ? ? ? ? ? for (Map.Entry<K,V> e : this)
? ? ? ? ? ? ? ? list.add(new AbstractMap.SimpleEntry<K,V>(e));
? ? ? ? ? ? return list;
? ? ? ? }
? ? ? ? // 返回Entry對應的Object[]數組
? ? ? ? public Object[] toArray() {
? ? ? ? ? ? return deepCopy().toArray();
? ? ? ? }
? ? ? ? // 返回Entry對應的T[]數組(T[]我們新建數組時,定義的數組類型)
? ? ? ? public <T> T[] toArray(T[] a) {
? ? ? ? ? ? return deepCopy().toArray(a);
? ? ? ? }
? ? }
}
```
說明:WeakHashMap和HashMap都是通過"拉鏈法"實現的散列表。它們的源碼絕大部分內容都一樣,這里就只是對它們不同的部分就是說明。
? WeakReference是“弱鍵”實現的哈希表。它這個“弱鍵”的目的就是:實現對“鍵值對”的動態(tài)回收。當“弱鍵”不再被使用到時,GC會回收它,WeakReference也會將“弱鍵”對應的鍵值對刪除。
? “弱鍵”是一個“弱引用(WeakReference)”,在Java中,WeakReference和ReferenceQueue 是聯合使用的。在WeakHashMap中亦是如此:如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。 接著,WeakHashMap會根據“引用隊列”,來刪除“WeakHashMap中已被GC回收的‘弱鍵’對應的鍵值對”。
? ? 另外,理解上面思想的重點是通過 expungeStaleEntries() 函數去理解。
## 第4部分 WeakHashMap遍歷方式
**4.1 遍歷WeakHashMap的鍵值對**
第一步:根據entrySet()獲取WeakHashMap的“鍵值對”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
```
// 假設map是WeakHashMap對象
// map中的key是String類型,value是Integer類型
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
? ? Map.Entry entry = (Map.Entry)iter.next();
? ? // 獲取key
? ? key = (String)entry.getKey();
? ? ? ? // 獲取value
? ? integ = (Integer)entry.getValue();
}
```
**4.2 遍歷WeakHashMap的鍵**
第一步:根據keySet()獲取WeakHashMap的“鍵”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
```
// 假設map是WeakHashMap對象
// map中的key是String類型,value是Integer類型
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
? ? ? ? // 獲取key
? ? key = (String)iter.next();
? ? ? ? // 根據key,獲取value
? ? integ = (Integer)map.get(key);
}
```
**4.3 遍歷WeakHashMap的值**
第一步:根據value()獲取WeakHashMap的“值”的集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
> // 假設map是WeakHashMap對象
// map中的key是String類型,value是Integer類型
Integer value = null;
Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
? ? value = (Integer)iter.next();
}
**WeakHashMap遍歷測試程序如下:**
```
import java.util.Map;
import java.util.Random;
import java.util.Iterator;
import java.util.WeakHashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Collection;
/*
* @desc 遍歷WeakHashMap的測試程序。
*? (01) 通過entrySet()去遍歷key、value,參考實現函數:
*? ? ? ? iteratorHashMapByEntryset()
*? (02) 通過keySet()去遍歷key、value,參考實現函數:
*? ? ? ? iteratorHashMapByKeyset()
*? (03) 通過values()去遍歷value,參考實現函數:
*? ? ? ? iteratorHashMapJustValues()
*
* @author skywang
*/
public class WeakHashMapIteratorTest {
? ? public static void main(String[] args) {
? ? ? ? int val = 0;
? ? ? ? String key = null;
? ? ? ? Integer value = null;
? ? ? ? Random r = new Random();
? ? ? ? WeakHashMap map = new WeakHashMap();
? ? ? ? for (int i=0; i<12; i++) {
? ? ? ? ? ? // 隨機獲取一個[0,100)之間的數字
? ? ? ? ? ? val = r.nextInt(100);
? ? ? ? ? ? key = String.valueOf(val);
? ? ? ? ? ? value = r.nextInt(5);
? ? ? ? ? ? // 添加到WeakHashMap中
? ? ? ? ? ? map.put(key, value);
? ? ? ? ? ? System.out.println(" key:"+key+" value:"+value);
? ? ? ? }
? ? ? ? // 通過entrySet()遍歷WeakHashMap的key-value
? ? ? ? iteratorHashMapByEntryset(map) ;
? ? ? ? // 通過keySet()遍歷WeakHashMap的key-value
? ? ? ? iteratorHashMapByKeyset(map) ;
? ? ? ? // 單單遍歷WeakHashMap的value
? ? ? ? iteratorHashMapJustValues(map);? ? ? ?
? ? }
? ? /*
? ? * 通過entry set遍歷WeakHashMap
? ? * 效率高!
? ? */
? ? private static void iteratorHashMapByEntryset(WeakHashMap map) {
? ? ? ? if (map == null)
? ? ? ? ? ? return ;
? ? ? ? System.out.println("\niterator WeakHashMap By entryset");
? ? ? ? String key = null;
? ? ? ? Integer integ = null;
? ? ? ? Iterator iter = map.entrySet().iterator();
? ? ? ? while(iter.hasNext()) {
? ? ? ? ? ? Map.Entry entry = (Map.Entry)iter.next();
? ? ? ? ? ? key = (String)entry.getKey();
? ? ? ? ? ? integ = (Integer)entry.getValue();
? ? ? ? ? ? System.out.println(key+" -- "+integ.intValue());
? ? ? ? }
? ? }
? ? /*
? ? * 通過keyset來遍歷WeakHashMap
? ? * 效率低!
? ? */
? ? private static void iteratorHashMapByKeyset(WeakHashMap map) {
? ? ? ? if (map == null)
? ? ? ? ? ? return ;
? ? ? ? System.out.println("\niterator WeakHashMap By keyset");
? ? ? ? String key = null;
? ? ? ? Integer integ = null;
? ? ? ? Iterator iter = map.keySet().iterator();
? ? ? ? while (iter.hasNext()) {
? ? ? ? ? ? key = (String)iter.next();
? ? ? ? ? ? integ = (Integer)map.get(key);
? ? ? ? ? ? System.out.println(key+" -- "+integ.intValue());
? ? ? ? }
? ? }
? ? /*
? ? * 遍歷WeakHashMap的values
? ? */
? ? private static void iteratorHashMapJustValues(WeakHashMap map) {
? ? ? ? if (map == null)
? ? ? ? ? ? return ;
? ? ? ? Collection c = map.values();
? ? ? ? Iterator iter= c.iterator();
? ? ? ? while (iter.hasNext()) {
? ? ? ? ? ? System.out.println(iter.next());
? ? ? }
? ? }
}
```
第5部分 WeakHashMap示例
下面通過實例來學習如何使用WeakHashMap
```
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Date;
import java.lang.ref.WeakReference;
/**
* @desc WeakHashMap測試程序
*
* @author skywang
* @email kuiwu-wang@163.com
*/
public class WeakHashMapTest {
? ? public static void main(String[] args) throws Exception {
? ? ? ? testWeakHashMapAPIs();
? ? }
? ? private static void testWeakHashMapAPIs() {
? ? ? ? // 初始化3個“弱鍵”
? ? ? ? String w1 = new String("one");
? ? ? ? String w2 = new String("two");
? ? ? ? String w3 = new String("three");
? ? ? ? // 新建WeakHashMap
? ? ? ? Map wmap = new WeakHashMap();
? ? ? ? // 添加鍵值對
? ? ? ? wmap.put(w1, "w1");
? ? ? ? wmap.put(w2, "w2");
? ? ? ? wmap.put(w3, "w3");
? ? ? ? // 打印出wmap
? ? ? ? System.out.printf("\nwmap:%s\n",wmap );
? ? ? ? // containsKey(Object key) :是否包含鍵key
? ? ? ? System.out.printf("contains key two : %s\n",wmap.containsKey("two"));
? ? ? ? System.out.printf("contains key five : %s\n",wmap.containsKey("five"));
? ? ? ? // containsValue(Object value) :是否包含值value
? ? ? ? System.out.printf("contains value 0 : %s\n",wmap.containsValue(new Integer(0)));
? ? ? ? // remove(Object key) : 刪除鍵key對應的鍵值對
? ? ? ? wmap.remove("three");
? ? ? ? System.out.printf("wmap: %s\n",wmap );
? ? ? ? // ---- 測試 WeakHashMap 的自動回收特性 ----
? ? ? ? // 將w1設置null。
? ? ? ? // 這意味著“弱鍵”w1再沒有被其它對象引用,調用gc時會回收WeakHashMap中與“w1”對應的鍵值對
? ? ? ? w1 = null;
? ? ? ? // 內存回收。這里,會回收WeakHashMap中與“w1”對應的鍵值對
? ? ? ? System.gc();
? ? ? ? // 遍歷WeakHashMap
? ? ? ? Iterator iter = wmap.entrySet().iterator();
? ? ? ? while (iter.hasNext()) {
? ? ? ? ? ? Map.Entry en = (Map.Entry)iter.next();
? ? ? ? ? ? System.out.printf("next : %s - %s\n",en.getKey(),en.getValue());
? ? ? ? }
? ? ? ? // 打印WeakHashMap的實際大小
? ? ? ? System.out.printf(" after gc WeakHashMap size:%s\n", wmap.size());
? ? }
}
```
## 運行結果
> wmap:{three=w3, one=w1, two=w2}
contains key two : true
contains key five : false
contains value 0 : false
wmap: {one=w1, two=w2}
next : two - w2
after gc WeakHashMap size:1