您好,登錄后才能下訂單哦!
如何深入解讀LinkedHashMap原理和源碼,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
LinkedHashMap 顧名思義,就是一個基于 HashMap 的雙向鏈表的集合。其中 HashMap 為數組加單向鏈表的結構,LinkedList 為雙向鏈表實現的。而 LinkedHashMap 正是二者的結合體。
首先,從源碼上來看,LinkedHashMap 繼承自 HashMap,所以 HashMap 有的大部分特性,LinkedHashMap 都有。比如:線程安全問題等。關于 HashMap 推薦閱讀《搞不懂 HashMap?只因你缺一個 HashMap 的原理機制流程圖!》。
順著 LinkedHashMap 的源碼往下看,你會發現 LinkedHashMap 提供了 5 個構造函數。基本上就是對 HashMap 的構造函數做了擴展,加了一個重要的參數 accessOrder,它代表的是一個訪問順序,后面會具體的講。
注意上圖,LinkedHashMap 還有一個改變就是多了兩個屬性:header 和 accessOrder。header 就是雙向鏈表的表頭元素。當 accessOrder 為 true 時,代表按訪問順序迭代,false 時表示按照插入順序。這個配圖來自網上,是基于 JDK1.6 的,現在 JDK8 的變化已經很大了。
LinkedHashMap 繼承了 HashMap 中大部分的方法,只是多了雙向鏈表。
LinkedHashMap 的 Entry 增加了用于維護順序的 before 和 after 變量,就是用來確定鏈表的前后節點。本來無序的數組 + 鏈表結構,通過 before 和 after 把它們關聯起來,變的有序。在 put 和 get 的時候維護好了這個雙向鏈表,遍歷的時候就有序了。
LinkedHashMap 的節點 Entry 繼承自 HashMap.Node,然后將單向鏈表改成了一個雙向鏈表。
LinkedHashMap 并沒有重寫任何 put 方法。但是其重寫了構建新節點的 newNode() 方法。
newNode() 會在 HashMap 的 putVal() 方法里被調用,putVal() 方法會在批量插入數據 putMapEntries(Map m, boolean evict) 或者插入單個數據 public V put(K key, V value) 時被調用。
LinkedHashMap 重寫了 newNode(),在每次構建新節點時,通過 linkNodeLast(p); 將新節點鏈接在內部雙向鏈表的尾部。
其中的 linkNodeLast 方法,會將新增的節點。如果,集合之前是空的,指定 head 節點;否則將新節點連接在鏈表的尾部。
另外 LinkedHashMap 的 afterNodeInsertion 方法也非常的重要,它會在新節點插入之后回調,根據 evict 和 first 判斷是否需要刪除最老插入的節點。
LinkedHashMap 還重寫了 afterNodeRemoval 這個方法。在刪除節點 e 時,同步將 e 從雙向鏈表上刪除。
另外 get() 和 getOrDefault() 方法也被重寫了。因為我們要根據 accessOrder 規則來調整具體的獲取方法。在 accessOrder 為 true 的情況下,要去回調 void afterNodeAccess(Node e) 函數。
在 afterNodeAccess() 函數中,會將當前被訪問到的節點 e,移動至內部的雙向鏈表的尾部。
通過上圖的解釋,你會發現。afterNodeAccess 方法埋下了隱患,會修改 modCount,因此當你正在 accessOrder=true 的模式下,迭代 LinkedHashMap 時,如果同時查詢訪問數據,也會導致 fail-fast,因為迭代的順序已經改變。
最后,總結一下。LinkedHashMap 相對于 HashMap 的源碼比,是很簡單的。因為大樹底下好乘涼。它繼承了 HashMap,僅重寫了幾個方法,以改變它迭代遍歷時的順序。這也是其與 HashMap 相比最大的不同。在每次插入數據,或者訪問、修改數據時,會增加節點、或調整鏈表的節點順序。以決定迭代時輸出的順序。
accessOrder,默認是 false,則迭代時輸出的順序是插入節點的順序。
若為 true,則輸出的順序是按照訪問節點的順序。
為 true 時,可以在這基礎之上構建一個 LruCache。
參考《手把手教你用LinkedHashMap打造FIFO和LRU緩存系統》
LinkedHashMap 并沒有重寫任何 put 方法。
但是其重寫了構建新節點的 newNode() 方法.在每次構建新節點時,將新節點鏈接在內部雙向鏈表的尾部
accessOrder=true 的模式下,在 afterNodeAccess() 函數中,會將當前被訪問到的節點 e,移動至內部的雙向鏈表的尾部。
值得注意的是,afterNodeAccess() 函數中,會修改 modCount,因此當你正在 accessOrder=true 的模式下,迭代 LinkedHashMap 時,如果同時查詢訪問數據,也會導致 fail-fast,因為迭代的順序已經改變。
nextNode() 就是迭代器里的next()方法。
該方法的實現可以看出,迭代 LinkedHashMap,就是從內部維護的雙鏈表的表頭開始循環輸出。
而雙鏈表節點的順序在LinkedHashMap的增、刪、改、查時都會更新。
以滿足按照插入順序輸出,還是訪問順序輸出。
它與 HashMap 比,還有一個小小的優化,重寫了 containsValue() 方法,直接遍歷內部鏈表去比對 value 值是否相等。
看完上述內容,你們掌握如何深入解讀LinkedHashMap原理和源碼的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。