LinkedHashMap分析
LinkedHashMap分析
這篇文章會分析一下 LinkedHashMap。
?
LinkedHashMap是啥
首先我們看一下這個類的定義:
1 public class LinkedHashMap<K,V> 2 extends HashMap<K,V> 3 implements Map<K,V>可以看到LinkedHashMap同樣實現了Map接口,但它同時還繼承了HashMap,所以天然就有HashMap自身的特性。
?
從名字上看? LinkedHashMap 比 HashMap 多了前面的“ Linked “, 那它們之間的區別在哪里呢?我們再看一段代碼:
1 /** 2 * LinkedHashMap的Entry對象,除了繼承于HashMap Node的4個屬性,額外添加了兩個屬性before,after用于維護雙向鏈表的前后結點關系 3 */ 4 static class Entry<K,V> extends HashMap.Node<K,V> { 5 Entry<K,V> before, after; 6 Entry(int hash, K key, V value, Node<K,V> next) { 7 super(hash, key, value, next); 8 } 9 }從這里可以看到相比較于HashMap的Node,除了繼承自HashMap Node數據結構中的hash、key、value、next四個屬性之外,LinkedHashMap的Node還多維護了兩個屬性:before,after。
這兩個屬性是用于維護一條獨立的雙向鏈表,而這個雙向鏈表中保存的僅僅是在LinkedHashMap中節點與節點的前后關系。
所以可以這么認為:LinkedHashMap是 "維護了節點之間前后順序的HashMap" 。
?
其他用到的屬性:
1 /** 2 * 雙向鏈表的頭結點(最近最少使用的結點) 3 */ 4 transient LinkedHashMap.Entry<K,V> head; 5 6 /** 7 * 雙向鏈表的尾結點(最近最多使用的結點) 8 */ 9 transient LinkedHashMap.Entry<K,V> tail; 10 11 /** 12 * LinkedHashMap核心屬性,該屬性定義了雙向鏈表中存儲結點的順序: 13 * 如果為true,則雙向鏈表按照結點的訪問順序維護前后結點關系(訪問、操作結點都會影響該結點在雙向鏈表的位置),這種方式實現了LRU算法 14 * 如果為false,則雙向鏈表僅僅按照結點的插入順序維護前后結點關系(只有操作結點的動作才會影響該結點在雙向鏈表的位置) 15 * 該值默認為false. 16 */ 17 final boolean accessOrder;這里需要著重注意第17行的accessOrder這個變量,LinkedHashMap通過在構造方法中傳入該參數的值(true/false)來控制雙向鏈表中維護節點的前后順序時是根據訪問順序維護還是插入順序維護。解釋一下這兩種順序有啥區別:
?假設一開始往Map中添加了3個節點? A、B、C,則此時雙向鏈表中維護節點的前后順序應該是? ?A -> B -> C,此時調用HashMap的get(key)方法訪問B節點:
如果 accessOrder = true, 那么按照訪問節點維護雙向鏈表中節點前后順序,此時節點的前后順序關系變更為? ?A -> C -> B;
如果accessOrder = false,那么節點的前后順序關系不變,依舊為?A -> B -> C。
當然,如果是插入、刪除節點,無論accessOrder設置為何值,雙向鏈表中節點的前后順序關系都會發生改變。比如在Map插入一個節點D
那么無論accessOrder為true還是false,雙向鏈表中節點的前后順序關系都會變更為? A -> B -> C -> D。
?
?
LinkedHashMap核心源碼分析(JDK Version 1.8)
?
1.構造方法?
1 /** 2 * 帶capacity和loadFactor的構造函數 3 * 4 * @param initialCapacity 自定義初始容量 5 * @param loadFactor 自定義負載因子 6 * @throws IllegalArgumentException 初始容量或負載因子為負數,拋出異常 7 * 8 */ 9 public LinkedHashMap(int initialCapacity, float loadFactor) { 10 super(initialCapacity, loadFactor); //調用HashMap的構造函數 11 accessOrder = false; 12 } 13 14 /** 15 * 只帶capacity參數的構造函數,此時loadFactor為默認的0.75 16 * 17 * @param initialCapacity 自定義初始容量 18 * @throws IllegalArgumentException 初始容量為負數,拋出異常 19 */ 20 public LinkedHashMap(int initialCapacity) { 21 super(initialCapacity); //調用HashMap的構造函數 22 accessOrder = false; 23 } 24 25 /** 26 * 無參數構造函數。初始容量為默認的16,負載因子為默認的0.75 27 */ 28 public LinkedHashMap() { 29 super(); //調用HashMap的構造函數 30 accessOrder = false; 31 } 32 33 34 /** 35 * 三個參數的構造函數,可以控制雙向鏈表的存儲方式 36 * 37 * @param initialCapacity 初始容量 38 * @param loadFactor 負載因子 39 * @param accessOrder 雙向鏈表維護的存儲順序 40 * @throws IllegalArgumentException 初始容量或負載因子為負數,拋出異常 41 */ 42 public LinkedHashMap(int initialCapacity, 43 float loadFactor, 44 boolean accessOrder) { 45 super(initialCapacity, loadFactor); //調用HashMap的構造方法 46 this.accessOrder = accessOrder; 47 }
2.LinkedHashMap在訪問和操作節點時特有的方法
因為LinkedHashMap維護了雙向鏈表,所以相比HashMap在訪問、插入、刪除節點時都要額外再對雙向鏈表中維護的節點前后關系進行操作。
結合注釋看應該很好理解,就不再啰嗦了。
?
LinkedHashMap的LRU特性
先講一下LRU的定義:LRU(Least Recently Used),即最近最少使用算法,最初是用于內存管理中將無效的內存塊騰出而用于加載數據以提高內存使用效率而發明的算法。
目前已經普遍用于提高緩存的命中率,如Redis、Memcached中都有使用。
為啥說LinkedHashMap本身就實現了LRU算法?原因就在于它額外維護的雙向鏈表中。
在上面已經提到過,在做get/put操作時,LinkedHashMap會將當前訪問/插入的節點移動到鏈表尾部,所以此時鏈表頭部的那個節點就是 "最近最少未被訪問"的節點。
舉個例子:
往一個空的LinkedHashMap中插入A、B、C三個結點,那么鏈表會經歷以下三個狀態:
1.? A? ?插入A節點,此時整個鏈表只有這一個節點,既是頭節點也是尾節點
2.? A? ->? B? ?插入B節點后,此時A為頭節點,B為尾節點,而最近最常訪問的節點就是B節點(剛被插入),而最近最少使用的節點就是A節點(相對B節點來講,A節點已經有一段時間沒有被訪問過)
3.? A? ->? B? ->? C? 插入C節點后,此時A為頭節點,C為尾節點,而最近最常訪問的節點就是C節點(剛被插入),最近最少使用的節點就是A節點 (應該很好理解了吧? : ))
那么對于緩存來講,A就是我最長時間沒訪問過的緩存,C就是最近才訪問過的緩存,所以當緩存隊列滿時就會從頭開始替換新的緩存值進去,從而保證緩存隊列中的緩存盡可能是最近一段時間訪問過的緩存,提高緩存命中率。
?
OK,到這里LinkedHashMap就分析完了,相比于HashMap還是比較容易理解的吧 : )
下一篇文章會分析 TreeMap 的具體實現。
?
轉載于:https://www.cnblogs.com/smartchen/p/9016176.html
總結
以上是生活随笔為你收集整理的LinkedHashMap分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 9,函数的初识
- 下一篇: transform css3 的使用及理