Java集合WeakHashMap源碼分析
簡介
WeakHashMap 繼承于AbstractMap,實現(xiàn)了Map接口。
和HashMap一樣,WeakHashMap 也是一個散列表,它存儲的內(nèi)容也是鍵值對(key-value)映射,而且鍵和值都可以是null。
不一樣的是,JDK1.8開始,HashMap中引入了紅黑樹,節(jié)點名從entry改成了node,而WeakHashMap還是沒有被修改,還是采用鏈表形式的拉鏈法解決哈希沖突。
所謂weak,就是WeakHashMap中存儲的鍵值是弱引用的,是很有可能被GC回收的,所以,WeakHashMap中需要對被GC的鍵的鍵值對進行清除,其實現(xiàn)原理:
WeakHashMap中有一個ReferenceQueue用來存儲被GC回收的弱鍵;
當每次操作WeakHashMap的時候,就會需要同步table和queue,通過同步的行為,就可以刪除table中已經(jīng)被回收了的鍵的鍵值對。
源碼分析
定義
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {}
字段
// 默認初始容量,和hashmap一樣 private static final int DEFAULT_INITIAL_CAPACITY = 16; // 最大容量 private static final int MAXIMUM_CAPACITY = 1 << 30; // 默認負載因子 private static final float DEFAULT_LOAD_FACTOR = 0.75f; // 存儲鍵值對鏈表頭節(jié)點的數(shù)組 Entry<K,V>[] table; // 當前節(jié)點數(shù)量 private int size; // 擴容閾值 private int threshold; // 加因子實際大小 private final float loadFactor; // 被垃圾回收的弱引用鍵隊列 private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); // 修改次數(shù) int modCount;
和參數(shù)和HashMap大致相同,不同的是,多了一個引用隊列,用來存儲被GC的引用,用于之后的同步。
構(gòu)造函數(shù)
// 初始化容量和加載因子的構(gòu)造函數(shù) 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); int capacity = 1; // 通過比較位移的方式,得到第一個大于等于設(shè)定容量的2的冪次的合法容量 while (capacity < initialCapacity) capacity <<= 1; // 這個newtbale就是初始化了一個capactiy大小的空數(shù)組 table = newTable(capacity); this.loadFactor = loadFactor; // 計算擴容閾值 threshold = (int)(capacity * loadFactor); } // 初始化容量的構(gòu)造 public WeakHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 默認構(gòu)造 public WeakHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } // 添加其他map的構(gòu)造 public WeakHashMap(Map<? extends K, ? extends V> m) { // 設(shè)定容量和加載因子 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); // 把節(jié)點都添加進去 putAll(m); }
內(nèi)部類
1.節(jié)點的結(jié)構(gòu)
// 繼承了弱引用,實現(xiàn)了Map.Entry,所以它的節(jié)點鍵值都是弱引用,不會防止GC private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { // 節(jié)點存儲值 V value; // 節(jié)點哈希值 final int hash; // 下一個節(jié)點引用 Entry<K,V> next; // 構(gòu)造,新建節(jié)點 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; } // 重寫了比較接口函數(shù),就比較類型和鍵值 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函數(shù),然會鍵值的哈希值而不是對象的哈希值 public int hashCode() { K k = getKey(); V v = getValue(); return Objects.hashCode(k) ^ Objects.hashCode(v); } public String toString() { return getKey() + "=" + getValue(); } }
2.迭代器
private abstract class HashIterator<T> implements Iterator<T> { // 當前索引 private int index; // 當前元素 private Entry<K,V> entry; // 上一次返回的元素 private Entry<K,V> lastReturned; // 實現(xiàn)fast-faiul機制 private int expectedModCount = modCount; // 指向下一個鍵值(強引用) private Object nextKey; // 當前節(jié)點(強引用) private Object currentKey; // 構(gòu)造 HashIterator() { index = isEmpty() ? 0 : table.length; } // 判斷是否存在下一個節(jié)點 public boolean hasNext() { Entry<K,V>[] t = table; // 如果下一個而節(jié)點是空的,就需要遍歷table,將下一個節(jié)點指向table中下一個不為空的頭節(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; } // 獲取下一個節(jié)點 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; } // 刪除當前節(jié)點 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; } } // 值遍歷 private class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } // 鍵的遍歷 private class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } // 鍵值對的遍歷 private class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } }
3.集合
// 鍵的集合 private class KeySet extends AbstractSet<K> { // 調(diào)用迭代器的接口 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(); } // 分割迭代器,用于并行 public Spliterator<K> spliterator() { return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); } } // 鍵值對集合 private class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o); } public int size() { return WeakHashMap.this.size(); } public void clear() { WeakHashMap.this.clear(); } // 深拷貝接口 private List<Map.Entry<K,V>> deepCopy() { List<Map.Entry<K,V>> list = new ArrayList<>(size()); // 將鍵值對都添加到新的鏈表當中 for (Map.Entry<K,V> e : this) list.add(new AbstractMap.SimpleEntry<>(e)); return list; } // 轉(zhuǎn)化為數(shù)組 public Object[] toArray() { return deepCopy().toArray(); } // 模板方法的數(shù)組 public <T> T[] toArray(T[] a) { return deepCopy().toArray(a); } // 分割迭代 public Spliterator<Map.Entry<K,V>> spliterator() { return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); } }
方法
1.哈希函數(shù)
// 獲取鍵的哈希值 final int hash(Object k) { int h = k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). // 這樣搞是為了盡可能地均勻吧 h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
2.元素獲取
// 獲取最新的表 private Entry<K,V>[] getTable() { // 之所以要獲取最新的表,是因為需要先刪除GC的Key expungeStaleEntries(); return table; } // 獲取對應(yīng)鍵的元素值 public V get(Object key) { // 如果key是null那么就用一個final的空對象,這樣保證每次null的對象相同 Object k = maskNull(key); // 獲取key的哈希值 int h = hash(k); // 獲取最新的表,在這里會觸發(fā)一次表的更新,就是將GC了的key給移除 Entry<K,V>[] tab = getTable(); // 根據(jù)哈希值獲取當前table中對應(yīng)的索引 int index = indexFor(h, tab.length); // 拿出節(jié)點 Entry<K,V> e = tab[index]; // 遍歷鏈表 while (e != null) { // 匹配值 if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } // 沒有找到就返回空 return null; }
3.元素添加
// 添加獲取修改鍵值 public V put(K key, V value) { // 這些操作和上面差不多 Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); // 遍歷鏈表,如果有相同的key,那就直接修改值 for (Entry<K,V> e = tab[i]; e != null; e = e.next) { if (h == e.hash && eq(k, e.get())) { V oldValue = e.value; if (value != oldValue) e.value = value; return oldValue; } } modCount++; Entry<K,V> e = tab[i]; // 數(shù)組頭添加新的節(jié)點,采用了頭插法 tab[i] = new Entry<>(k, value, queue, h, e); if (++size >= threshold) // 如果當數(shù)量大于等于閾值則進行擴容 resize(tab.length * 2); return null; }
4.刪除被GC的節(jié)點
WeakHashTable就是通過這個函數(shù)實現(xiàn)弱引用被GC后的表中節(jié)點的回收。
private void expungeStaleEntries() { // 遍歷引用隊列中被標記回收得值 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; // 刪除節(jié)點 while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // 將鍵對應(yīng)得值指向空,這樣就可以讓GC來回收原來得對象 size--; break; } prev = p; p = next; } } } }
5.擴容
擴容的大致其實和HashMap差不多
// 擴容到新得容量 void resize(int newCapacity) { Entry<K,V>[] oldTable = getTable(); int oldCapacity = oldTable.length; // 邊界判斷 if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } // 新建一個標準大小的數(shù)組 Entry<K,V>[] newTable = newTable(newCapacity); // 將舊數(shù)組上的數(shù)據(jù)復(fù)制過去 transfer(oldTable, newTable); // 更新引用 table = newTable; // 查看size是不是大于,擴容閾值的一半,如果不是,說明size又變小了,不需要擴容了 if (size >= threshold / 2) { // 更新擴容閾值 threshold = (int)(newCapacity * loadFactor); } else { // 更新GC后的key expungeStaleEntries(); // 返回原有大小的表 transfer(newTable, oldTable); table = oldTable; } } // 將原表復(fù)制到目標表 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; // 遍歷鏈表,再將節(jié)點放到新表的對應(yīng)位置 while (e != null) { Entry<K,V> next = e.next; Object key = e.get(); if (key == null) { // 用于GC e.next = null; e.value = null; size--; } else { // 獲取到對應(yīng)的索引 int i = indexFor(e.hash, dest.length); e.next = dest[i]; dest[i] = e; } e = next; } } }
6.元素刪除
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--; // 如果是頭節(jié)點 if (prev == e) tab[i] = next; else prev.next = next; return e.value; } prev = e; e = next; } return null; }
總結(jié)
- 大致的1.7的哈希表差不多,采用拉鏈法解決哈希沖突,只有鏈表,采用頭插法,包括初始容量、擴容閾值和大小。
- 表中的節(jié)點繼承了弱引用,這說明它的引用的鍵是會被垃圾回收的。
- 主要的區(qū)別就是它再對表進行修改的時候,都會調(diào)用expungeStaleEntries函數(shù),用來刪除那些已經(jīng)被垃圾回收了的鍵,所對應(yīng)的鍵值對。需要刪除的鍵會存放在ReferenceQueue 中,每次去獲取需要被刪除的key。
- 和其他集合的重要區(qū)別,WeakHashMap沒有實現(xiàn)克隆和序列化的接口。
到此這篇關(guān)于Java集合WeakHashMap源碼分析的文章就介紹到這了,更多相關(guān)WeakHashMap源碼分析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何利用Java輸出鏈表中倒數(shù)第k個結(jié)點
這篇文章主要給大家介紹了關(guān)于如何利用Java輸出鏈表中倒數(shù)第k個結(jié)點的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用java具有一定的參考學習價值,需要的朋友可以參考下2021-12-12java中i=i++和j=i++的區(qū)別小結(jié)
這篇文章主要給大家介紹了關(guān)于java中i=i++和j=i++區(qū)別的相關(guān)資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04SpringBoot導(dǎo)出PDF的四種實現(xiàn)方法詳解
在Spring?Boot應(yīng)用程序中實現(xiàn)PDF導(dǎo)出功能,可以選擇多種庫和技術(shù)棧,本文為大家整理了四種常見的方法,感興趣的小伙伴可以參考一下2025-02-02Redis Java Lettuce驅(qū)動框架原理解析
這篇文章主要介紹了Redis Java Lettuce驅(qū)動框架原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12在RedHat系統(tǒng)上安裝JDK與Tomcat的步驟
這篇文章主要介紹了在RedHat系統(tǒng)上安裝Java與Tomcat的步驟,同樣適用于CentOS等RedHat系的Linux系統(tǒng),需要的朋友可以參考下2015-11-11SpringCloud Gateway HttpWebHandlerAdapter鏈路調(diào)用請求流程介
Spring Cloud Gateway旨在為微服務(wù)架構(gòu)提供一種簡單有效的、統(tǒng)一的 API 路由管理方式。Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系中的網(wǎng)關(guān),它不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全、監(jiān)控/埋點和限流等2022-10-10ehcache開源緩存框架_動力節(jié)點Java學院整理
Ehcache是現(xiàn)在最流行的純Java開源緩存框架,這篇文章主要介紹了ehcache框架的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07