天天看點

【JDK】JDK源碼分析-LinkedHashMap

概述

前文「JDK源碼分析-HashMap(1)」分析了 HashMap 主要方法的實作原理(其他問題以後分析),本文分析下 LinkedHashMap。

先看一下 LinkedHashMap 的類繼承結構圖:

【JDK】JDK源碼分析-LinkedHashMap

可以看到 LinkedHashMap 繼承了 HashMap。

我們知道 HashMap 是無序的,即疊代器的順序與插入順序沒什麼關系。而 LinkedHashMap 在 HashMap 的基礎上增加了順序:分别為「插入順序」和「通路順序」。即周遊 LinkedHashMap 時,可以保持與插入順序一緻的順序;或者與通路順序一緻的順序。

LinkedHashMap 内部如何實作這兩種順序的呢?它是通過一個雙連結清單來維持的。是以可以将 LinkedHashMap 了解為「雙連結清單 + 散清單」,或者“有序的散清單”。 

代碼分析

節點類 Entry

LinkedHashMap 内部有一個嵌套類 Entry,它繼承自 HashMap 中的 Node 類,如下:

該 Entry 類就是 LinkedHashMap 中的節點類。可以看到,它在 Node 類的基礎上又增加了 before 和 after 兩個變量,它們儲存的是節點的前驅和後繼(從字面意思也可以進行推測),進而來維護 LinkedHashMap 的順序。

成員變量

構造器

構造器1:

這裡的 super() 方法調用了 HashMap 的無參構造器。該構造器方法構造了一個容量為 16(預設初始容量)、負載因子為 0.75(預設負載因子)的空 LinkedHashMap,其順序為插入順序。

構造器 2、3、4、5:

可以看到上面幾個構造器都是通過調用父類(HashMap)的構造器方法初始化,對此不再進行分析。前面 4 個構造器的 accessOrder 變量預設值都為 false;最後一個稍微不一樣,它的 accessOrder 可以在初始化時指定,即指定 LinkedHashMap 的順序(插入或通路順序)。

put 方法

LinkedHashMap 本身沒有實作 put 方法,它通過調用父類(HashMap)的方法來進行讀寫操作。這裡再貼下 HashMap 的 put 方法:

但是,LinkedHashMap 重寫了該方法:

此外,上文分析 HashMap 時提到兩個回調方法:afterNodeAccess 和 afterNodeInsertion。它們在 HashMap 中是空的:

這裡描述了進行該操作前後的兩種情況。可以看到,該方法執行後,節點 p 被移到了 list 的末尾。

get 方法

LinkedHashMap 重寫了 HashMap 的 get 方法,主要是為了維持通路順序,代碼如下:

這裡的 getNode 方法是父類的(HashMap)。若 accessOrder 為 true(即指定為通路順序),則将通路的節點移到清單末尾。

LinkedHashMap 中重寫的 afterNodeInsertion 方法:

removeNode 方法是父類 HashMap 中的。

LinkedHashMap 重寫了該方法:

代碼演練

LinkedHashMap 用法

我們知道 HashMap 是無序的,例如:

而若換成 LinkedHashMap,則可以保持插入的順序:

指定 LinkedHashMap 的順序為通路順序:

實作 LRU 緩存

這裡定義的 LRUCache 類中,對 removeEldestEntry 方法進行了重寫,當緩存中的容量大于 2,時會把最早插入的元素 "bush" 删除。是以隻剩下兩個值。 

小結

1. LinkedHashMap 繼承自 HashMap,其結構可以了解為「雙連結清單 + 散清單」;

2. 可以維護兩種順序:插入順序或通路順序;

3. 可以友善的實作 LRU 緩存;

4. 線程不安全。

Stay hungry, stay foolish.

【JDK】JDK源碼分析-LinkedHashMap

繼續閱讀