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