【深入學習java集合系列】LinkedHashMap的底層實現
最近寫到LeetCode上的某一題LRUCache。可以採用LinkedHashMap實現,通過重寫removeEldestEntry方法,即可實現。
LinkedHashMap map; public LRUCache(int capacity) { map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) { // 定義put後的移除規則,大於容量就刪除eldest protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }; } public int get(int key) { return map.containsKey(key) ? (int)map.get(key) : -1 ; } public void put(int key, int value) { map.put(key, value); }
但是,這樣實在太耍賴了,所以決定研究LinkedHashMap的底層到底是如何實現的?原始碼應該是最好的學習程式碼了。
1、繼承關係
- publicclass LinkedHashMap<K,V>
- extends HashMap<K,V>
- implements Map<K,V>
linkedhashmap繼承hashMap,底層的主要儲存結構是Hashmap的table。LinkedHashMap實現與HashMap的不同之處在於,後者維護著一個運行於所有條目的雙重連結列表。此連結列表定義了迭代順序,該迭代順序可以是插入順序或者是訪問順序。
2、成員變數
- privatetransient Entry<K,V> header;
- privatefinalboolean accessOrder;
首先header是一個雙向連結串列,具體體現在Entry結構中,我們知道hashmap的Entry是一個單向連結串列,只有一個next屬性。
其次就是accessOrder,它決定了linkedhashmap中的元素在遍歷的時候的輸出順序(插入順序或者是訪問順序)。
3、Entry物件
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
4、建構函式
- //預設accessOrder為false
- //呼叫HashMap建構函式
- public LinkedHashMap() {
- super();
- accessOrder = false;
- }
- //如果想實現LRU演算法,參考這個建構函式
- public LinkedHashMap(int initialCapacity, float loadFactor,
- boolean accessOrder) {
- super(initialCapacity, loadFactor);
- this.accessOrder = accessOrder;
- }
- //模板方法模式,HashMap建構函式裡面的會呼叫init()方法
- //初始化的時候map裡沒有任何Entry,讓header.before = header.after = header
- void init() {
- header = new Entry(-1, null, null, null);
- header.before = header.after = header;
- }
5、儲存資料
LinkedHashMap並未重寫父類HashMap的put方法,而是重寫了父類HashMap的put方法呼叫的子方法void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K
key, V value, int bucketIndex),提供了自己特有的雙向連結列表的實現。
- //LinkedHashMap沒有put(K key, V value)方法,只重寫了被put呼叫的addEntry方法
- //1是HashMap裡原有的邏輯,23是LinkedHashMap特有的
- void addEntry(int hash, K key, V value, int bucketIndex) {
- createEntry(hash, key, value, bucketIndex);
- Entry eldest = header.after;
- //3.如果有必要,移除LRU裡面最老的Entry,否則判斷是否該resize
- if (removeEldestEntry(eldest)) {
- removeEntryForKey(eldest.key);
- } else {
- if (size >= threshold)
- resize(2 * table.length);
- }
- }
- void createEntry(int hash, K key, V value, int bucketIndex) {
- //1.同HashMap一樣:在Entry陣列+next連結串列結構裡面加入Entry
- HashMap.Entry old = table[bucketIndex];
- Entry e = new Entry(hash, key, value, old);
- table[bucketIndex] = e;
- //2.把新Entry也加到header連結串列結構裡面去
- e.addBefore(header);
- size++;
- }
- //預設是false,我們可以重寫此方法
- protectedboolean removeEldestEntry(Map.Entry eldest) {
- returnfalse;
- }
- privatestaticclass Entry extends HashMap.Entry {
- //連結串列插入元素四個步驟,對著圖看
- privatevoid addBefore(Entry existingEntry) {
- after = existingEntry; //1
- before = existingEntry.before; //2
- before.after = this; //3
- after.before = this; //4
- }
- }
- //如果走到resize,會呼叫這裡重寫的transfer
- //HashMap裡面的transfer是n * m次運算,LinkedHashtable重寫後是n + m次運算
- void transfer(HashMap.Entry[] newTable) {
- int newCapacity = newTable.length;
- //直接遍歷header連結串列,HashMap裡面是遍歷Entry陣列
- for (Entry e = header.after; e != header; e = e.after) {
- int index = indexFor(e.hash, newCapacity);
- e.next = newTable[index];
- newTable[index] = e;
- }
- }
LinkedHashMap重寫了父類HashMap的get方法,實際在呼叫父類getEntry()方法取得查詢的元素後,再判斷當排序模式accessOrder為true時,記錄訪問順序,將最新訪問的元素新增到雙向連結串列的表頭,並從原來的位置刪除。由於的連結串列的增加、刪除操作是常量級的,故並不會帶來效能的損失。
- //重寫了get(Object key)方法
- public V get(Object key) {
- //1.呼叫HashMap的getEntry方法得到e
- Entry e = (Entry) getEntry(key);
- if (e == null)
- returnnull;
- //2.LinkedHashMap牛B的地方
- e.recordAccess(this);
- return e.value;
- }
- // 繼承了HashMap.Entry
- privatestaticclass Entry extends HashMap.Entry {
- //1.此方法提供了LRU的實現
- //2.通過12兩步,把最近使用的當前Entry移到header的before位置,而LinkedHashIterator遍歷的方式是從header.after開始遍歷,先得到最近使用的Entry
- //3.最近使用是什麼意思:accessOrder為true時,get(Object key)方法會導致Entry最近使用;put(K key, V value)/putForNullKey(value)只有是覆蓋操作時會導致Entry最近使用。它們都會觸發recordAccess方法從而導致Entry最近使用
- //4.總結LinkedHashMap迭代方式:accessOrder=false時,迭代出的資料按插入順序;accessOrder=true時,迭代出的資料按LRU順序+插入順序
- // HashMap迭代方式:橫向陣列 * 豎向next連結串列
- void recordAccess(HashMap m) {
- LinkedHashMap lm = (LinkedHashMap) m;
- //如果使用LRU演算法
- if (lm.accessOrder) {
- lm.modCount++;
- //1.從header連結串列裡面移除當前Entry
- remove();
- //2.把當前Entry移到header的before位置
- addBefore(lm.header);
- }
- }
- //讓當前Entry從header連結串列消失
- privatevoid remove() {
- before.after = after;
- after.before = before;
- }
- }
7、刪除資料
- // 繼承了HashMap.Entry
- privatestaticclass Entry extends HashMap.Entry {
- //LinkedHashMap沒有重寫remove(Object key)方法,重寫了被remove呼叫的recordRemoval方法
- //這個方法的設計也和精髓,也是模板方法模式
- //HahsMap remove(Object key)把資料從橫向陣列 * 豎向next連結串列裡面移除之後(就已經完成工作了,所以HashMap裡面recordRemoval是空的實現呼叫了此方法
- //但在LinkedHashMap裡面,還需要移除header連結串列裡面Entry的after和before關係
- void recordRemoval(HashMap m) {
- remove();
- }
- //讓當前Entry從header連結串列消失
- privatevoid remove() {
- before.after = after;
- after.before = before;
- }
- }
- privateabstractclass LinkedHashIterator implements Iterator {
- //從header.after開始遍歷
- Entry nextEntry = header.after;
- Entry nextEntry() {
- if (modCount != expectedModCount)
- thrownew ConcurrentModificationException();
- if (nextEntry == header)
- thrownew NoSuchElementException();
- Entry e = lastReturned = nextEntry;
- nextEntry = e.after;
- return e;
- }
- }
九.總結
- LinkedHashMap繼承HashMap,結構2裡資料結構的變化交給HashMap就行了。
- 結構1裡資料結構的變化就由LinkedHashMap裡重寫的方法去實現。
- 簡言之:LinkedHashMap比HashMap多維護了一個連結串列。
相關推薦
【深入學習java集合系列】LinkedHashMap的底層實現
最近寫到LeetCode上的某一題LRUCache。可以採用LinkedHashMap實現,通過重寫removeEldestEntry方法,即可實現。 LinkedHashMap map; public LRUCache(int capacity) {
【深入理解Java集合框架】紅黑樹講解(上)
時間復雜度 row lee tel framework 關系 eight logs return 來源:史上最清晰的紅黑樹講解(上) - CarpenterLee 作者:CarpenterLee(轉載已獲得作者許可,如需轉載請與原作者聯系) 文中所有圖片點擊之後均可查看大
【深入理解Java 虛擬機器】學習筆記一
目錄 執行時資料區域 根索引演算法 垃圾回收演算法 垃圾收集器 雙親委派模型 JDK命令列工具Jstack 和 JConsole 1、執行時資料區域 執行緒共享:方法區、堆 執行緒私有:虛擬機器棧、本地方法棧、程式計數器 2、根索引演
【Java集合系列】 總體框架
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
【深入理解java集合】-ArryList實現原理
一、ArrayList簡介 1、概述 ArrayList是基於陣列實現的,是一個動態陣列,其容量能自動增長,類似於C語言中的動態申請記憶體,動態增長記憶體。 ArrayList不是執行緒安全的,只能用在單執行緒環境下,多執行緒環境下可以考慮用Collections.
【深入理解java集合】-LinkedList實現原理
一、LinkeddList簡介 1、LinkedList概述 LinkedList是一個一個繼承於AbstractSequentialList,並實現了List介面和Deque介面的雙端連結串列。 LinkedList底層的連結串列結構使它支援高效的插入和刪除操作,
【深入理解java集合】-TreeMap實現原理
一、紅黑樹介紹 1、R-B Tree概念 紅黑樹(Red Black Tree,簡稱R-B Tree) 是一種自平衡二叉查詢樹,它雖然是複雜的,但它的最壞情況執行時間也是非常良好的,並且在實踐中是高效的: 它可以在O(log n)時間內做查詢,插入和刪除,這裡的n 是
【Java集合系列】 總體框架
根據上面的類圖,我們可以把java的所有集合分成三大類,其中Set集合類類似於一個糖罐子,把一個物件新增到Set集合裡面的時候,Set集合無法記住新增這個元素的順序,所以Set裡面的元素不能重複,否則系統無法準確識別這個元素;List集合非常像一個數組,她可以記住每次新增元素的順序,可以重複,只是List的長
【深入理解Java虛擬機器】學習小結
第一章 走近Java 摘書 Java各個版本新特性 1.0:Java1.0提供了一個純解釋執行的Java虛擬機器實現(Sun Classic VM)。JDK1.0版本的代表技術包括:Java虛擬機
【Java集合系列】---總體框架
集合--童年的美好時光集合,忽然讓小編想起那段美好的學生時光,集合第一次遇見她的時候,小編當年還是一個懵懂的丫頭,也不曾想過會在計算機的世界再次相遇,再回首,集合在數學中是一個基本概念,集合就是“一堆東
【Java集合類】 LinkedHashMap(有序的map)獲取第一個元素和最後一個元素
獲取LinkedHashMap中的頭部元素(最早新增的元素): 時間複雜度O(1) public <K, V> Entry<K, V> getHead(LinkedHashMap<K, V> map) { retu
【java集合系列】---HashSet
在前面的博文中,小編主要簡單介紹了java集合中的總體框架,以及list介面中典型的集合ArrayList和LinkedList,接著,我們來看set的部分集合,set集合和數學意義上的集合沒有差別,作為集合,可以容納多個元素,而且,集合裡面沒有重複的元素,Set集合是Col
深入理解JAVA集合系列四:ArrayList源碼解讀
結束 了解 數組下標 size new 數組元素 開始 ini rem 在開始本章內容之前,這裏先簡單介紹下List的相關內容。 List的簡單介紹 有序的collection,用戶可以對列表中每個元素的插入位置進行精確的控制。用戶可以根據元素的整數索引(在列表中的位置)訪
深入理解JAVA集合系列三:HashMap的死循環解讀
現在 最新 star and 場景 所有 image cap 時也 由於在公司項目中偶爾會遇到HashMap死循環造成CPU100%,重啟後問題消失,隔一段時間又會反復出現。今天在這裏來仔細剖析下多線程情況下HashMap所帶來的問題: 1、多線程put操作後,get操作導
【由淺入深理解java集合(二)】——集合 Set
找到 str rip ges 地址 view 包括 細節 無法 上一篇文章介紹了Set集合的通用知識。Set集合中包含了三個比較重要的實現類:HashSet、TreeSet和EnumSet。本篇文章將重點介紹這三個類。 一、HashSet類 HashSet簡介
【深入理解java虛擬機器】第0集--Java記憶體區域和java記憶體模型
首先我們清楚【記憶體區域】和【記憶體模型】是兩個不一樣的概念。當時我電面阿里的時候,面試官讓我講講記憶體模型的理解,我巴拉巴拉說了一通方法區-堆分割槽,垃圾演算法,面試官耐心的聽我說完就把電話掛了。 【記憶體區域】對應的是jvm程序。jvm啟動之後,自身是一個大的程序,作業
深入學習java集合:JAVA集合類主要介面
Iterator介面主要用於遍歷 Collection 集合中的元素,Iterator物件也被稱為迭代器。Iterator介面隱藏了各種 Collection 實現類的底層細節,嚮應用程式提供了遍歷 Collection 集合元素的統一程式設計介面。Iterator僅用於遍歷集合,Iter
【深入理解Java虛擬機器】Java記憶體區域模型、物件建立過程、常見OOM
本文內容來源於《深入理解Java虛擬機器》一書,非常推薦大家去看一下這本書。最近開始看這本書,打算再開一個相關係列,來總結一下這本書中的重要知識點。呃呃呃,說好的那個圖片請求框架呢~ 不要急哈,因為這個請求框架設計的內容還是比較廣的,目前業餘時間正在編寫當中,弄好了之後就會
【深入理解Java虛擬機器】垃圾回收機制
本文內容來源於《深入理解Java虛擬機器》一書,非常推薦大家去看一下這本書。本系列其他文章:1、垃圾回收要解決的問題垃圾收集(Garbage Collection,GC),要設計一個GC,需要考慮解決下面三件事情:(1)哪些記憶體需要回收?(2)什麼時候回收?(3)如何回收?
【深入理解Java虛擬機器】類載入機制
本文內容來源於《深入理解Java虛擬機器》一書,非常推薦大家去看一下這本書。本系列其他文章:【深入理解Java虛擬機器】垃圾回收機制1、類載入機制概述虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Jav