Java類集框架 —— LinkedHashMap源碼分析

前言

我們知道HashMap底層是采用數組+單向線性鏈表/紅黑樹來實現的,HashMap在擴容或者鏈表與紅黑樹轉換過程時可能會改變元素的位置和順序。如果需要保存元素存入或訪問的先后順序,那就需要采用LinkedHashMap了。

LinkedHashMap結構

LinkedHashMap繼承自HashMap,它的所有操作和HashMap類似,底層結構也和HashMap一樣,只不過為了維護元素的存入/訪問順序,增加了一個雙向鏈表。

LinkedHashMap結構圖

LinkedHashMap由數組、單向線性鏈表、紅黑樹、雙向線性鏈表組成。如上圖:灰色區域為數組,藍色節點和藍色箭頭為單向鏈表的引用關系,綠色節點和綠色箭頭為紅黑樹的引用關系,節點中的數字依次表示元素的存入/訪問順序,由橙色的雙向箭頭表示雙向鏈表的引用關系。

注:在JDK1.7及之前HashMap中沒有紅黑樹,LinkedHashMap中也不存在紅黑樹。另在JDK1.6及之前,HashMap中的鏈表為單向環形鏈表,LinkedHashMap中中的單向鏈表和雙向鏈表都是環形鏈表。在JDK1.8,LinkedHashMap中可能會存在紅黑樹,同時單向鏈表和雙向鏈表都是線性的。本文是基于JDK1.8來分析的。

LinkedHashMap源碼分析

基本屬性:

transient LinkedHashMap.Entry<K,V> head;    // 雙向鏈表頭節點
transient LinkedHashMap.Entry<K,V> tail;    // 雙向鏈表尾節點
final boolean accessOrder;                    // 是否按照訪問順序排序復制代碼

head和tail分別記錄了雙向鏈表的頭節點和尾節點,遍歷時通過headtail就可以按照存入/訪問的順序來取數據。

accessOrder用以表示LinkedHashMap是否按照訪問順序來排序,為true的話表示按照訪問順序排序,為false表示按照存入順序排序,默認為false

構造函數:

// 無參構造
public LinkedHashMap() {super();accessOrder = false;
}
// 給定初始容量
public LinkedHashMap(int initialCapacity) {super(initialCapacity);accessOrder = false;
}
// 給定初始容量和加載因子
public LinkedHashMap(int initialCapacity, float loadFactor) {super(initialCapacity, loadFactor);accessOrder = false;
}
// 給定初始容量、加載因子、是否按訪問先后排序
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder;
}復制代碼

構造函數都是調用父類HashMap的構造函數。前3個都默認accessOrderfalseLinkedHashMap內部按照存入順序排序。最后一個構造函數可以指定accessOrder的值。

增:

LinkedHashMap添加數據要調用了父類的HashMapput方法,在HashMap的源碼中,put方法存入元素后,調用了afterNodeAccessafterNodeInsertion方法,這兩個方法在HashMap中都是空方法,LinkedHashMap實現了這兩個方法:

void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last;// 如果按照訪問順序排序,并且添加的元素e不是雙向鏈表的尾節點if (accessOrder && (last = tail) != e) {LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;if (b == null)head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;++modCount;}
}復制代碼

afterNodeAccess方法的邏輯就是將當前節點e移動到雙向鏈表的尾部。每次LinkedHashMap中有元素被訪問時,就會按照訪問先后來排序,先訪問的在雙向鏈表中靠前,越后訪問的越接近尾部。當然只有當accessOrdertrue時,才會執行這個操作。

void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);}
}復制代碼

afterNodeInsertion方法意思是evicttrue時刪除雙向鏈表的頭節點。

通過afterNodeAccessafterNodeInsertion這兩個方法,如果當LinkedHashMap的容量達到一定量時,需要保存它的size不變,那么每次添加一個元素到雙向鏈表的尾部,就要刪除一個雙向鏈表頭部的元素,這相當于實現了LruCache的策略。

刪:

刪除元素同樣也是調用了HashMapremove方法,在remove方法中,調用了afterNodeRemoval方法。

void afterNodeRemoval(Node<K,V> e) { // unlinkLinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.before = p.after = null;if (b == null)head = a;elseb.after = a;if (a == null)tail = b;elsea.before = b;
}復制代碼

afterNodeRemoval方法就是將e節點從雙向鏈表中刪除,更改e前后節點引用關系,使之重新連成完整的雙向鏈表。

改:

LinkedHashMap更改元素的value值,仍是調用put方法,涉及到的邏輯可以看上面的afterNodeAccessafterNodeInsertion這兩個方法。

查:

LinkedHashMap自己實現了get方法。

public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return null;if (accessOrder)afterNodeAccess(e);return e.value;
}復制代碼

邏輯非常簡單,直接調用HashMapgetNode方法,如果需要按照訪問先后排序,調用afterNodeAccess更新雙向鏈表排序。

總結

LinkedHashMap繼承了HashMap的所有特性,唯一的區別就是LinkedHashMap是一個有序的映射集合,而HashMap則是無序的。LinkedHashMap實現排序的原理就是再內部增加了一個雙向鏈表來記錄元素的存入/訪問順序。LinkedHashMap內部是記錄的是存入還是訪問順序取決于關鍵屬性accessOrder,默認是按存入順序記錄。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454981.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454981.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454981.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

apache 支持.htaccess重寫url

1. httpd.conf 添加&#xff1a; <Directory />Options Indexes FollowSymLinks MultiviewsAllowOverride allRequire all grantedRewriteEngine On</Directory> 開啟&#xff1a; 在phpinfo里找到&#xff1a; 說明開啟成功。 2.httpd-vhosts.conf &#xff08;開…

oom 如何避免 高并發_【高并發】高并發環境下如何防止Tomcat內存溢出?看完我懂了!!...

【高并發】高并發環境下如何防止Tomcat內存溢出&#xff1f;看完我懂了&#xff01;&#xff01;發布時間&#xff1a;2020-04-19 00:47,瀏覽次數&#xff1a;126, 標簽&#xff1a;Tomcat寫在前面隨著系統并發量越來越高&#xff0c;Tomcat所占用的內存就會越來越大&#xff0…

[JSOI2008]最小生成樹計數

OJ題號&#xff1a;  BZOJ1016 題目大意&#xff1a;   給定一個無向帶權圖&#xff0c;求最小生成樹的個數。 思路&#xff1a;   先跑一遍最小生成樹&#xff0c;統計相同權值的邊出現的個數。   易證不同的最小生成樹&#xff0c;它們不同的那一部分邊的權值實際上是…

vuex webpack 配置_vue+webpack切換環境和打包之后服務器配置

import axios from ‘axios‘import store from ‘../store/index‘const rootUrl process.env.API_ROOT//創建axios實例const service axios.create({timeout:30000 //超時時間})//添加request攔截器service.interceptors.request.use(config >{if (Object.keys(config.hea…

redis基本用法學習(C#調用FreeRedis操作redis)

FreeRedis屬于常用的基于.net的redis客戶端&#xff0c;EasyCaching中也提供適配FreeRedis的包。根據參考文獻4中的說法&#xff0c;FreeRedis和CsRedis算是近親&#xff08;都是GitHub中賬號為2881099下的開源項目&#xff09;&#xff0c;因此其用法特別相似。FreeRedis的主要…

opencv:圖像的基本變換

0.概述 圖像變換的基本原理都是找到原圖和目標圖的像素位置的映射關系&#xff0c;這個可以用坐標系來思考&#xff0c;在opencv中&#xff0c; 圖像的坐標系是從左上角開始(0,0)&#xff0c;向右是x增加方向(cols)&#xff0c;向下時y增加方向(rows)。 普通坐標關系&#xff1…

FFMpeg在Windows環境下的編譯

1&#xff0e;安裝MinGW &#xff08;1&#xff09;下載文件&#xff1a;MinGW-5.1.4.exe&#xff0c; &#xff08;2&#xff09;安裝時選擇下列組件&#xff1a; binutils-2.19.1-mingw32-bin.tar.gz gcc-core-3.4.5-20060117-3.tar.gz gcc-g-3.4.5-20060117-3.tar.gz …

中通知設置響鈴_主動切斷干擾源——手機“通知”精細化管理

上周去參加我福福幼兒園的母親節活動&#xff0c;內容是孩子和家長一起穿手鏈。期間我發現和我同桌的一個家長的手機不停在響&#xff0c;當然伴隨著注意力被打斷。不僅是這位家長自己&#xff0c;連我也受到了干擾。于是職業病又犯了&#xff0c;我悄悄的看了一眼這位家長的手…

OCM_第十九天課程:Section9 —》Data Guard _ DATA GUARD 原理/DATA GUARD 應用/DATA GUARD 搭建...

注&#xff1a;本文為原著&#xff08;其內容來自 騰科教育培訓課堂&#xff09;。閱讀本文注意事項如下&#xff1a;1&#xff1a;所有文章的轉載請標注本文出處。2&#xff1a;本文非本人不得用于商業用途。違者將承當相應法律責任。3&#xff1a;該系列文章目錄列表&#xf…

python安裝各種插件

http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip 感受&#xff1a;如果編輯pip真的一直出問題&#xff0c;考慮降成32位的進行安裝。畢竟合理搭配比木桶突出有用。轉載于:https://www.cnblogs.com/osmondwang/p/7307678.html

編寫數學公式的好工具

2019獨角獸企業重金招聘Python工程師標準>>> http://private.codecogs.com/latex/eqneditor.php 轉載于:https://my.oschina.net/yizhichao/blog/1542153

dev gridview 打印列數過多_R語言:如何將多張統計圖繪制在一張上面

在使用R語言進行數據可視化的時候&#xff0c;常常需要將多張統計圖表繪制在同一張圖上面&#xff0c;從而更高效地傳遞信息&#xff0c;下面我們就來一起看看具體如何實現。一、使用R語言自帶的函數繪制的圖像R語言本身就已經內置了許多繪圖函數&#xff0c;能夠滿足較為基本的…

264分析兩大利器 和 視頻系列下載:264VISA和Elecard StreamEye Tools

學了264有將近3個月有余&#xff0c;好多時候都在學習老畢的書和反復看JM86的代碼&#xff0c;最近才找到264分析兩大利器&#xff1a;264VISA和Elecard StreamEye Tools。不由得感嘆&#xff0c;恨不逢同時。 簡單的說下這兩個軟件&#xff1a; 264visa 強力的h264實時分析工具…

[轉]vue全面介紹--全家桶、項目實例

慢慢了解vue及其全家桶的過程 原文http://blog.csdn.net/zhenghao35791/article/details/67639415 簡介 “簡單卻不失優雅&#xff0c;小巧而不乏大匠”。 2016年最火的前端框架當屬Vue.js了&#xff0c;很多使用過vue的程序員這樣評價它&#xff0c;“vue.js兼具angular.js和R…

opencv 星空_opencv如何將大于5000像素點的輪廓繪制出來?

contourArea函數的運用。具體例子可以看下面的。《如何獲得物體的主要方向&#xff1f;》代碼略解&#xff1a;1、讀入圖片&#xff0c;尋找輪廓&#xff1b;//讀入圖像&#xff0c;轉換為灰度Mat img imread("e:/sandbox/pca1.jpg");Mat bw;cvtColor(img, bw, COLO…

TS 188字節流結構圖

應該說真正了解TS&#xff0c;還是看了朋友推薦的《數字電視業務信息及其編碼》一書之后&#xff0c;MPEG2 TS和數字電視是緊密不可分割的&#xff0c;值得總結一下其中的一些關系。 ISO/IEC&#xff0d;13818&#xff0d;1&#xff1a;系統部分&#xff1b; ISO/IEC&#xff…

二進制安裝mysql 5.7、mariadb (附yum安裝方式)

前言&#xff1a;本文以mariadb為例進行講解&#xff0c;安裝mysql同理&#xff0c;并以通過測試。安裝前查找系統已安裝的相關包&#xff08;rpm -qa|grep -e "mysql" -e "mariadb"&#xff09;并進行卸載。1、準備mariadb存儲數據庫文件的目錄。mkdir -p…

GLSL/C++ 實現濾鏡效果

入門效果之浮雕 "浮雕"圖象效果是指圖像的前景前向凸出背景。常見于一些紀念碑的雕刻上。要實現浮雕事實上很easy。我們把圖象的一個象素和左上方的象素進行求差運算。并加上一個灰度。這個灰度就是表示背景顏色。這里我們設置這個插值為128 (圖象RGB的值是0-255)。同…

cv mat的shape_pybind11—opencv圖像處理(numpy數據交換)

前言C opencv中圖像和矩陣的表示采用Mat類&#xff0c;比如imread()讀取的結果就是返回一個Mat對象。對于python而言&#xff0c;numpy 通常用于矩陣運算&#xff0c; 矩陣&#xff0c;圖像表示為numpy.ndarray類。因此&#xff0c;想要將python numpy.ndarray的數據傳遞到C op…

H.264算法的優化策略

文章來源&#xff1a; http://www.tichinese.com/Article/Video/200909/2150.html 編輯&#xff1a;小乙哥 1 代碼優化的主要方法 通過代碼移植能夠獲得在DSP上初步運行的代碼&#xff0c;但是它由于沒有考慮到DSP自身的硬件特點&#xff0c;不適合DSP強大的并行處理能力&#…