聲明,本文用得是jdk1.8
前面已經講了Collection的總覽和剖析List集合以及散列表、Map集合、紅黑樹還有HashMap基礎了:php
本篇主要講解LinkedHashMap~html
看這篇文章以前最好是有點數據結構的基礎:java
固然了,若是講得有錯的地方還請你們多多包涵並不吝在評論去指正~算法
LinkedHashMap數據結構圖:c#
ps:圖片來源網絡,侵刪~微信
首先咱們來看看類繼承圖:網絡
我簡單翻譯了一下頂部的註釋(我英文水平渣,若是有錯的地方請多多包涵~歡迎在評論區下指正)數據結構
從頂部翻譯咱們就能夠概括總結出HashMap幾點:post
同時也給我帶了幾個疑問:測試
但願能夠在看源碼的過程當中能夠解決掉我這兩個疑問~那接下來就開始吧~
下面我列舉就這兩個比較重要的:
這就印證了咱們的LinkedHashMap底層確確實實是散列表和雙向鏈表~
LinkedHashMap.Entry
再也不是Node
.能夠發現,LinkedHashMap有5個構造方法:
下面咱們來看看構造方法的定義是怎麼樣的:
從構造方法上咱們能夠知道的是:LinkedHashMap默認使用的是插入順序
本來我是想要找put方法,看看是怎麼實現的,後來沒找着,就奇了個怪~
再頓了一下,原來LinkedHashMap和HashMap的put方法是同樣的!LinkedHashMap繼承着HashMap,LinkedHashMap沒有重寫HashMap的put方法
因此,LinkedHashMap的put方法和HashMap是同樣的。
若是沒看過HashMap就是這麼簡單【源碼剖析】的同窗,可進去看看~
固然了,在建立節點的時候,調用的是LinkedHashMap重寫的方法~
get方法也是多了:判斷是否爲訪問順序~~~
講到了這裏,感受咱們能夠簡單測試一波了:
首先咱們來看看已插入順序來進行插入和遍歷:
public static void insertOrder() { // 默認是插入順序 LinkedHashMap<Integer,String> insertOrder = new LinkedHashMap(); String value = "關注公衆號Java3y"; int i = 0; insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); insertOrder.put(i++, value); //遍歷 Set<Integer> set = insertOrder.keySet(); for (Integer s : set) { String mapValue = insertOrder.get(s); System.out.println(s + "---" + mapValue); } }
測試一波:
接着,咱們來測試一下以訪問順序來進行插入和遍歷:
public static void accessOrder() { // 設置爲訪問順序的方式 LinkedHashMap<Integer,String> accessOrder = new LinkedHashMap(16, 0.75f, true); String value = "關注公衆號Java3y"; int i = 0; accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); // 遍歷 Set<Integer> sets = accessOrder.keySet(); for (Integer key : sets) { String mapValue = accessOrder.get(key); System.out.println(key + "---" + mapValue); } }
代碼看似是沒有問題,可是運行會出錯的!
前面在看源碼註釋的時候咱們就發現了:在AccessOrder的狀況下,使用get方法也是結構性的修改!
爲了簡單看出他倆的區別,下面我就直接用key來進行看了~
如下是訪問順序的測試:
public static void accessOrder() { // 設置爲訪問順序的方式 LinkedHashMap<Integer,String> accessOrder = new LinkedHashMap(16, 0.75f, true); String value = "關注公衆號Java3y"; int i = 0; accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); accessOrder.put(i++, value); // 訪問一下key爲3的元素再進行遍歷 accessOrder.get(3); // 遍歷 Set<Integer> sets = accessOrder.keySet(); for (Integer key : sets) { System.out.println(key ); } }
測試結果:
如下是插入順序的測試(代碼就不貼了,和上面幾乎同樣):
咱們能夠這樣理解:最經常使用的將其放在鏈表的最後,不經常使用的放在鏈表的最前~
這個知識點以個人理解而言,它這個訪問順序在LinkedHashMap若是不重寫用處並不大~它是用來給別的實現進行擴展的
removeEldestEntry(Map.Entry<K,V> eldest)
方法,重寫它能夠刪除最久未被使用的元素!!afterNodeInsertion(boolean evict)
方法,新增時判斷是否須要刪除最久未被使用的元素!!
去網上搜了幾篇資料,都是講LRUMap的實現的(也就是對LinkedHashMap進行擴展),有興趣的同窗可參考下列連接:
對於remove方法,在LinkedHashMap中也沒有重寫,它調用的仍是父類的HashMap的remove()
方法,在LinkedHashMap中重寫的是:afterNodeRemoval(Node<K,V> e)
這個方法
固然了,在remove的時候會涉及到上面重寫的方法:
Set<Map.Entry<K,V>> entrySet()
是被重寫的了
看到了這裏,咱們就知道爲啥註釋說:初始容量對遍歷沒有影響
由於它遍歷的是LinkedHashMap內部維護的一個雙向鏈表,而不是散列表(固然了,鏈表雙向鏈表的元素都來源於散列表)
LinkedHashMap比HashMap多了一個雙向鏈表的維護,在數據結構而言它要複雜一些,閱讀源碼起來比較輕鬆一些,由於大多都由HashMap實現了..
閱讀源碼的時候咱們會發現多態是無處不在的~子類用父類的方法,子類重寫了父類的部分方法便可達到不同的效果!
newNode()
方法重寫了。LinkedHashMap調用父類的put方法,裏面回調的是重寫後的newNode()
,從而達到目的!LinkedHashMap能夠設置兩種遍歷順序:
對於訪問順序,它是LRU(最近最少使用)算法的實現,要使用它要麼重寫LinkedListMap的幾個方法(removeEldestEntry(Map.Entry<K,V> eldest)
和afterNodeInsertion(boolean evict)
),要麼是擴展成LRUMap來使用,否則設置爲訪問順序(access-ordered)的用處不大~
LinkedHashMap遍歷的是內部維護的雙向鏈表,因此說初始容量對LinkedHashMap遍歷是不受影響的
參考資料:
明天要是無心外的話,可能會寫TreeMap,敬請期待哦~~~~
文章的目錄導航:https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠 關注微信公衆號:Java3y。爲了你們方便,剛新建了一下 qq羣:742919422,你們也能夠去交流交流。謝謝支持了!但願能多介紹給其餘有須要的朋友