Java LinkedHashMap類原始碼解析

2021-09-20 05:10:51 字數 3875 閱讀 9880

linkedhashmap繼承了hashmap,他在hashmap的基礎上增加了乙個雙向鍊錶的結構,鍊錶預設維持key插入的順序,重複的key值插入不會改變順序,適用於使用者需要返回乙個順序相同的map物件的情況。還可以生成access-order順序的版本,按照最近訪問順序來儲存,剛被訪問的結點處於鍊錶的末尾,適合lru,put get compute merge都算作一次訪問,其中put key值相同的結點也算作一次訪問,replace只有在換掉乙個鍵值對的時候才算一次訪問,putall產生的訪問順序取決於原本map的迭代器實現。

在插入鍵值對時,可以通過對removeeldestentry重寫來實現新鍵值對插入時自動刪除最舊的鍵值對

擁有hashmap提供的方法,迭代器因為是通過遍歷雙向鍊錶,所以額外開銷與size成正比與capacity無關,因此選擇過大的初始大小對於遍歷時間的增加沒有hashmap嚴重,後者的遍歷時間依賴與capacity。

同樣是非執行緒安全方法,對於linkedhashmap來說,修改結構的操作除了增加和刪除鍵值對外,還有對於access-order時進行了access導致迭代器順序改變,主要是get操作,對於插入順序的來說,僅僅修改乙個已有key值的value值不是乙個修改結構的操作,但對於訪問順序,put和get已有的key值會改變順序。迭代器也是fail-fast設計,但是fail-fast只是乙個除錯功能,乙個設計良好的程式不應該出現這個錯誤

因為hashmap加入了treenode,所以現在linkedhashmap也有這個功能

以下描述中的鍊錶,若無特別說明都是指linkedhashmap的雙向鍊錶

先來看一下基本結構,每個鍵值對加入了前後指標,集合加入了頭尾指標來形成雙向鍊錶,accessorder代表鍊錶是以訪問順序還是插入順序儲存

static class entryextends hashmap.node

}/**

* the head (eldest) of the doubly linked list.頭部

*/transient linkedhashmap.entryhead;

/*** the tail (youngest) of the doubly linked list.尾部

*/transient linkedhashmap.entrytail;

//true訪問順序 false插入順序

final boolean accessorder;

然後是幾個內部方法。linknodelast將p連線到鍊錶尾部

private void linknodelast(linkedhashmap.entryp) 

}

transferlinks用dst替換src

private void transferlinks(linkedhashmap.entrysrc,

linkedhashmap.entrydst)

reinitialize在呼叫hashmap方法的基礎上,將head和tail設為null

void reinitialize()

newnode生成乙個linkedhashmap結點,next指向e,插入到linkedhashmap鍊錶末端

nodenewnode(int hash, k key, v value, nodee)

replacementnode根據原結點生成乙個linkedhashmap結點替換原結點

nodereplacementnode(nodep, nodenext)

newtreenode生成乙個treenode結點,next指向next,插入到linkedhashmap鍊錶末端

treenodenewtreenode(int hash, k key, v value, nodenext)

replacementtreenode根據結點p生成乙個新的treenode,next設為給定的next,替換原本的p

treenodereplacementtreenode(nodep, nodenext)

afternoderemoval從linkedhashmap的鏈上移除結點e

void afternoderemoval(nodee)

afternodeinsertion可能移除最舊的結點,需要evict為true同時鍊錶不為空同時removeeldestentry需要重寫

void afternodeinsertion(boolean evict) 

}

afternodeaccess在訪問過後將結點e移動到鍊錶尾部,需要map是access-order,若移動成功則增加modcount

void afternodeaccess(nodee) 

tail = p;//結點e移動到鍊錶尾部

++modcount;//因為有access-order下結點被移動,所以增加modcount

}}

建構函式方面,accessorder預設是false插入順序,初始大小為16,負載因子為0.75,這裡是同hashmap。複製構造也是呼叫了hashmap.putmapentries方法

containsvalue遍歷鍊錶尋找相等的value值,這個操作一定不會造成結構改變

public boolean containsvalue(object value) 

return false;

}

get方法復用hashmap的getnode方法,若找到結點且map是訪問順序時,要將訪問的結點放到鍊錶最後,若沒找到則返回null。而getordefault僅有的區別是沒找到時返回defaultvalue

public v get(object key) 

public v getordefault(object key, v defaultvalue)

clear方法在hashmap的基礎上要把head和tail設為null

public void clear()

removeeldestentry在put和putall插入鍵值對時呼叫,原本是一定返回false的,如果要自動刪除最舊的鍵值對要返回true,需要進行重寫。比如下面這個例子,控制size不能超過100

private static final int max_entries = 100;

protected boolean removeeldestentry(map.entry eldest)

下面兩個方法和hashmap相似,返回key的set和value的collection還有返回鍵值對的set,這個是直接引用,所以對它們的remove之類的修改會直接反饋到linkedhashmap上

public setkeyset() 

return ks;//返回key值的set

}public collectionvalues()

return vs;//返回乙個包含所有value值的collection

}public set> entryset()

檢查hashmap的putval方法,我們可以看到在找到了相同key值並修改value值時會呼叫afternodeaccess,對於access-order會改變結點順序

if (e != null)

Java LinkedHashMap原始碼解讀

map 介面的雜湊表和鏈結列表實現,具有可預知的迭代順序。此實現與 hashmap 的不同之處在於,後者維護著乙個執行於所有條目的雙重鏈結列表。此鏈結列表定義了迭代順序,該迭代順序通常就是將鍵插入到對映中的順序 插入順序 注意,如果在對映中 重新插入 鍵,則插入順序不受影響。如果在呼叫 m.put ...

azkaban web server原始碼解析

azkaban主要用於hadoop相關job任務的排程,但也可以應用任何需要排程管理的任務,可以完全代替crontab。azkaban主要分為web server 任務上傳,管理,排程 executor server 接受web server的排程指令,進行任務執行 1.資料表 projects 工...

JDK LinkedHashMap原始碼解析

今天來分析一下jdk linkedhashmap的源 public class linkedhashmapextends hashmapimplements map可以看到,linkedhashmap繼承自hashmap,並且也實現了map介面,所以linkedhashmap沿用了hashmap的大...