Java 集合系列13之 WeakHashMap詳細介紹(源碼解析)和使用示例

概要

這一章,我們對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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容