1. 程式人生 > >LinkedHashMap原始碼分析與LRU實現

LinkedHashMap原始碼分析與LRU實現

LinkedHashMap可認為是雜湊表和連結列表綜合實現,並允許使用null值和null鍵。LinkedHashMap實現與HashMap的不同之處在於,LinkedHashMap維護著一個運行於所有條目的雙重連結列表。此連結列表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序。 LinkedHashMap的實現不是同步的。如果多個執行緒同時訪問LinkedHashMap,而其中至少一個執行緒從結構上修改了該對映,則它必須保持外部同步。

1.LinkedHashMap的儲存結構  


LinkedHashMap中加入了一個head頭結點,將所有插入到該LinkedHashMap中的Entry按照插入的先後順序(accessOrder標誌位預設為false)依次加入到以head為頭結點的雙向迴圈連結串列的尾部。

LinkedHashMap實際上就是HashMap和LinkedList兩個集合類的儲存結構的結合。在LinkedHashMapMap中,所有put進來的Entry都儲存在如圖所示的雜湊表中,但它又額外定義了一個以head為頭結點的空的雙向迴圈連結串列,每次put進來Entry,除了將其儲存到對雜湊表中對應的位置上外,還要將其插入到雙向迴圈連結串列的尾部。

下面我們來分析LinkedHashMap的原始碼。

2.LinkedHashMap成員變數

LinkedHashMap採用的hash演算法和HashMap相同,但它重新定義了陣列中儲存的元素Entry,該Entry除了儲存當前物件的引用外,還儲存了其上一個元素before和下一個元素after的引用

,從而在雜湊表的基礎上又構成了雙向連結列表。

  1. 1.//雙向迴圈連結串列的頭結點,整個LinkedHashMap中只有一個header,  
  2. 2.//(此連結串列不同於HashMap裡面的那個next連結串列)  
  3. 3.//它將雜湊表中所有的Entry貫穿起來,header中不儲存key-value對,只儲存前後節點的引用  
  4. 4.   privatetransient Entry<K,V> header;    
  5. 5.    
  6. 6.//雙向連結串列中元素排序規則的標誌位。  
  7. 7.//accessOrder為false,表示按插入順序排序  
  8. 8.//accessOrder為true,表示按訪問順序排序  
  9. 9.    privatefinalboolean accessOrder;    
  10. 10.    
  11. 11.    
  12. 12/**   
  13. 13. * LinkedHashMap的Entry元素。   
  14. 14. * 繼承HashMap的Entry元素,又儲存了其上一個元素before和下一個元素after的引用。   
  15. 15. */
  16. 16.privatestaticclass Entry<K,V> extends HashMap.Entry<K,V> {      
  17. 17.    Entry<K,V> before, after;      
  18. 18.    ……  //Entry類涉及到的方法,下面會繼續分析  
  19. 19.}    

3.建構函式

LinkedList一共提供了五個構造方法。

  1. 1.// 構造方法1,構造一個指定初始容量和載入因子的、按照插入順序的LinkedList  
  2. 2.//載入因子取預設的0.75f    
  3. 3.    
  4. 4.public LinkedHashMap(int initialCapacity, float loadFactor) {    
  5. 5.    super(initialCapacity, loadFactor);    
  6. 6.    accessOrder = false;    
  7. 7.}    
  8. 8.    
  9. 9.// 構造方法2,構造一個指定初始容量的LinkedHashMap,取得鍵值對的順序是插入順序  
  10. 10.//載入因子取預設的0.75f    
  11. 11.    
  12. 12.public LinkedHashMap(int initialCapacity) {    
  13. 13.    super(initialCapacity);    
  14. 14.    accessOrder = false;    
  15. 15.}    
  16. 16.    
  17. 17.// 構造方法3,用預設的初始化容量和載入因子建立一個LinkedHashMap,取得鍵值對的順序是插入順序  
  18. 18.//載入因子取預設的0.75f    
  19. 19.    
  20. 20.public LinkedHashMap() {    
  21. 21.    super();    
  22. 22.    accessOrder = false;    
  23. 23.}    
  24. 24.    
  25. 25.// 構造方法4,通過傳入的map建立一個LinkedHashMap,容量為預設容量(16)和  
  26. 26.//(map.zise()/DEFAULT_LOAD_FACTORY)+1的較大者,載入因子為預設值0.75  
  27. 27.    
  28. 28.public LinkedHashMap(Map<? extends K, ? extends V> m) {    
  29. 29.    super(m);    
  30. 30.    accessOrder = false;    
  31. 31.}    
  32. 32.    
  33. 33.// 構造方法5,根據指定容量、載入因子和指定連結串列中的元素排序的規則  建立一個LinkedHashMap  
  34. 34.public LinkedHashMap(int initialCapacity,    
  35. 35.             float loadFactor,    
  36. 36.                         boolean accessOrder) {    
  37. 37.    super(initialCapacity, loadFactor);    
  38. 38.    this.accessOrder = accessOrder;    
  39. 39.}    

我們已經知道LinkedHashMap的Entry元素繼承HashMap的Entry,提供了雙向連結串列的功能。在HashMap的構造器中,最後會呼叫init()方法,進行相關的初始化,這個方法在HashMap的實現中是空方法(感嘆模板模式的精妙!),只是提供給子類實現相關的初始化呼叫。LinkedHashMap重寫了init()方法,在呼叫父類的構造方法完成構造後,進一步實現了對其元素Entry的初始化操作。分析init()方法,的確是對header進行了初始化,並構造成一個雙向迴圈連結串列(和LinkedList的儲存結構是一樣的)。

  1. 1.void init() {    
  2. 2.    header = new Entry<K,V>(-1nullnullnull);    
  3. 3.    header.before = header.after = header;    
  4. 4.}    

4.元素儲存

LinkedHashMap重寫了父類HashMap的put方法呼叫的子方法void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的雙向連結列表的實現。

  1. 1.//覆寫HashMap中的addEntry方法,LinkedHashmap並沒有覆寫HashMap中的put方法,  
  2. 2.    //而是覆寫了put方法所呼叫的addEntry方法和recordAccess方法,  
  3. 3.    //put方法在插入的key已存在的情況下,會呼叫recordAccess方法,  
  4. 4.    //在插入的key不存在的情況下,要呼叫addEntry插入新的Entry  
  5. 5.void addEntry(int hash, K key, V value, int bucketIndex) {    
  6. 6.    //建立新的Entry,並插入到LinkedHashMap中  
  7. 7.    createEntry(hash, key, value, bucketIndex);    
  8. 8.    
  9. 9.    //雙向連結串列的第一個有效節點(header後的那個節點)為近期最少使用的節點  
  10. 10.    Entry<K,V> eldest = header.after;    
  11. 11.    //如果有必要,則刪除掉該近期最少使用的節點,  
  12. 12.    //這要看對removeEldestEntry的覆寫,由於預設為false,因此預設是不做任何處理的。  
  13. 相關推薦

    LinkedHashMap原始碼分析LRU實現

    LinkedHashMap可認為是雜湊表和連結列表綜合實現,並允許使用null值和null鍵。LinkedHashMap實現與HashMap的不同之處在於,LinkedHashMap維護著一個運行於所有條目的雙重連結列表。此連結列表定義了迭代順序,該迭代順序可以是插入

    LinkedHashMap原始碼分析實現LRU演算法

    PS: 要先了解HashMap的實現原理HashMap原始碼分析 一、簡單介紹 public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

    redis原始碼分析思考(十七)——有序集合型別的命令實現(t_zset.c)

        有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時

    redis原始碼分析思考(十六)——集合型別的命令實現(t_set.c)

        集合型別是用來儲存多個字串的,與列表型別不一樣,集合中不允許有重複的元素,也不能以索引的方式來通過下標獲取值,集合中的元素還是無序的。在普通的集合上增刪查改外,集合型別還實現了多個集合的取交集、並集、差集,集合的命令如下表所示: 集合命

    redis原始碼分析思考(十五)——雜湊型別的命令實現(t_hash.c)

        雜湊型別又叫做字典,在redis中,雜湊型別本身是一個鍵值對,而雜湊型別裡面也存貯著鍵值對,其對應關係是,每個雜湊型別的值對應著一個鍵值對或多對鍵值對,如圖所示: 雜湊型別命令 命令 對應操

    redis原始碼分析思考(十四)——列表型別的命令實現(t_list.c)

        列表型別是用來存貯多個字串物件的結構。一個列表可以存貯232-1個元素,可以對列表兩端進行插入(push)、彈出(pop),還可以獲取指定範圍內的元素列表、獲取指定索引的元素等等,它可以靈活的充當棧和佇列的角色。下面列出列表的命令: 列

    redis原始碼分析思考(十三)——字串型別的命令實現(t_string.c)

        在對字串操作的命令中,主要有增加刪查該、批處理操作以及編碼的轉換命令,現在列出對字串物件操作的主要常用命令: 常用命令表 命令 對應操作 時間複雜度

    redis原始碼分析思考(十七)——有序集合型別的命令實現(t_set.c)

        有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時間複

    手撕MyBatis底層原始碼分析實現

    MyBatis Hiberante 簡介 什麼是 MyBatis ? MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單

    Java BAT大型公司面試必考技能視訊教程之HashMap原始碼分析實現

    視訊通過以下四個方面介紹了HASHMAP的內容 一、 什麼是HashMap Hash雜湊將一個任意的長度通過某種演算法(Hash函式演算法)轉換成一個固定的值。 MAP:地圖 x,y 儲存 總結:通過HASH出來的值,然後通過值定位到這個MAP,然後value儲存到這個M

    面試必考之HashMap原始碼分析實現

    以下是JDK1.8之前版本的原始碼簡介 一、什麼是HashMap? Hash:雜湊將一個任意的長度通過某種(hash函式演算法)演算法轉換成一個固定值。 Map:儲存的集合、類似於地圖X,Y座

    B/S結構,服務器端的結構分析部分實現

    生成 因此 puts kit 服務器 請求 cati 集成 servers 1.1 簡述 瀏覽器在訪問一個IP地址的時候,一般會自動在地址前方加上HTTP:// 表示其基於http協議訪問。 一般而言,http跟tcp在本質上沒有區別。 tcp連接的過程,在JAVA語言中

    FIFOLRU實現(Java)

    固定 blank per shu static 置換 ted clas als 一、概述 在學操作系統的時候,會接觸到頁面緩存調度算法。緩存不可能是無限大的,所以會涉及到一些置換策略,來保證緩存的命中率。常見的有:FIFO、LRU、LFU、OPT策略等。 1、緩存置換算法

    PHP 控制反轉依賴注入詳細分析程式碼實現

    PHP有很多的設計模式,比如單例模式,訂閱模式,策略模式,工廠模式,觀察者模式,這些設計模式其實無非都是為了讓程式簡化,容易維護,模組間解耦。現在我們來講講PHP的另外一種設計模式,控制反轉/依賴注入,這兩者其實是同一個概念,只是凶不同的角度去解釋的而已。 依賴注入:是從需要實現的業務邏輯上面去

    【Android】原始碼分析 - LRUCache快取實現原理

    一、Android中的快取策略 一般來說,快取策略主要包含快取的新增、獲取和刪除這三類操作。如何新增和獲取快取這個比較好理解,那麼為什麼還要刪除快取呢?這是因為不管是記憶體快取還是硬碟快取,它們的快取大小都是有限的。當快取滿了之後,再想其新增快取,這個時候就需要刪除一些舊的快取

    gh0st原始碼分析遠控的編寫(一)

        再過幾天期末考試了,還有好多要複習。。蛋都快碎了。最近在看老狼的gh0st核心程式設計,想了很久要不要寫文章,最後還是覺得很有必要,原因過一會講。     先送上老狼的gh0st無加密(lxe格式)視訊下載地址:

    gh0st原始碼分析遠控的編寫(四)

      真的很久很久了,距離上一次寫gh0st的文章(http://www.mmcyy.com),過去有大半年了。總算有一個時間,我放下手裡所有的活,能夠繼續把這份努力延續下去。     以後對於gh0st的文章,就是一個一個模組的分析。原本gh

    gh0st原始碼分析遠控的編寫(三)

    好久不見。距離上次寫gh0st來有好久了,一是期末考試,忙不開,二是後來電腦壞了,幾天沒能上網。     昨天總算是把電腦修好了,雖說沒到一切重頭開始的地步,但是也重灌各種東西花了很久。閒下來的時間,我就來繼續分析gh0st的原始碼吧。    

    gh0st原始碼分析遠控的編寫(二)

    上次說了那麼多,基本上就是一個叫“大局觀”的東西,只有腦子裡有了一個軟體的設計、執行思路,才能把一個一個類寫出來,組合在一起。 Gh0st的作者是一個對程式碼有很好掌控的人,他對程式碼的組合,類之間的關係,面向物件的思想有很深入的理解。而對我們看原始碼的人

    redis原始碼分析思考(十九)——AOF持久化

        為了解決持久化檔案很龐大以及會阻塞伺服器的 情況,redis提出一種新的持久化方案:AOF持久化。AOF持久化是redis儲存資料的另外一種方式,全稱Append Only File,與RDB持久化不同的是,AOF持久化是隻儲存從客戶端鍵入