Java中的LinkedHashMap源碼詳解
LinkedHashMap
特點(diǎn):
底層數(shù)據(jù)結(jié)構(gòu):
- 數(shù)組加鏈表用來(lái)存儲(chǔ)數(shù)據(jù);
- header雙向鏈表用來(lái)實(shí)現(xiàn)數(shù)據(jù)插入有序或者訪問(wèn)有序;

繼承關(guān)系:
public class LinkedHashMap<K,V>
extends HashMap<K,V> //繼承了HashMap
implements Map<K,V>//實(shí)現(xiàn)了Map接口
{- 默認(rèn)數(shù)組大?。?6 ==>繼承父類(lèi)
- loadFactor(默認(rèn)加載因子):0.75 ==>繼承父類(lèi)
基本屬性:下面為L(zhǎng)inkedHashMap特有,別的屬性全部繼承HashMap;
private transient Entry<K,V> header; //頭結(jié)點(diǎn)
private final boolean accessOrder;//順序性; true(訪問(wèn)有序); false(插入有序)header如何初始化:header初始化需要調(diào)用重寫(xiě)后的 init()方法,創(chuàng)建一個(gè)不存儲(chǔ)數(shù)據(jù)的entry實(shí)體,而init方法是在父類(lèi)的構(gòu)造函數(shù)中被調(diào)用,子類(lèi)的初始化都會(huì)調(diào)用父類(lèi)的構(gòu)造函數(shù),從而實(shí)現(xiàn)了header的初始化;
//子類(lèi)重寫(xiě)方法;
@Override
void init() {
header = new Entry<>(-1, null, null, null); //hash值為-1;
header.before = header.after = header;
}
//父類(lèi)構(gòu)造函數(shù):
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init(); //在LinkedHashMap起作用,用來(lái)初始化header;
}構(gòu)造函數(shù):均是調(diào)用父類(lèi)對(duì)應(yīng)的構(gòu)造函數(shù)
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);//調(diào)用父類(lèi)的構(gòu)造函數(shù)
accessOrder = false;
}
//只指定數(shù)組大小
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
//指定數(shù)組大小。加載因子,以及確定使用何種有序
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}增長(zhǎng)方式:繼承父類(lèi),2*table.length; CRUD(增刪改查): put: 調(diào)用的是父類(lèi)的put方法,但是對(duì)put方法中一些相關(guān)函數(shù)進(jìn)行重寫(xiě);
// (父類(lèi)HashMap實(shí)現(xiàn))
public V put(K key, V value) {
if (table == EMPTY_TABLE) {//如果table為空,創(chuàng)建默認(rèn)數(shù)組
inflateTable(threshold);
}
if (key == null)//對(duì)key進(jìn)行特殊處理,key為null總在0號(hào)角標(biāo)鏈表中
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length); //通過(guò)hash找到對(duì)應(yīng)角標(biāo)
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
//遍歷該角標(biāo)倆表,找到對(duì)應(yīng)key值
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
//找到key值將value值進(jìn)行更新,并返回舊的value值
V oldValue = e.value;
e.value = value;
e.recordAccess(this);//此方法在子類(lèi)LinkedHashMap重寫(xiě),發(fā)揮作用;
//針對(duì)LinkedHashMap,確定是插入有序,還是插入有序;
//如果是訪問(wèn)有序,將原節(jié)點(diǎn)刪除,并添加到header最后
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);//現(xiàn)有集合中沒(méi)找到key,那么創(chuàng)建一個(gè)新的entry實(shí)體
return null;
}
//(當(dāng)前類(lèi)LinkedHashMap實(shí)現(xiàn))
void addEntry(int hash, K key, V value, int bucketIndex) {
//目前此函數(shù)并沒(méi)有看出與父類(lèi)的區(qū)別
super.addEntry(hash, key, value, bucketIndex);//調(diào)用父類(lèi)創(chuàng)建entry實(shí)體
// Remove eldest entry if instructed
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {//總返回false;這句等于無(wú)效代碼,留作后用;
removeEntryForKey(eldest.key);
}
}
//(父類(lèi)HashMap實(shí)現(xiàn))
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
//如果集合中元素個(gè)數(shù)已經(jīng)大于閾值,那么進(jìn)行擴(kuò)容;
resize(2 * table.length);//二倍擴(kuò)容
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length); 找到新的key對(duì)應(yīng)的數(shù)組角標(biāo)
}
createEntry(hash, key, value, bucketIndex);//創(chuàng)建entry實(shí)體,子類(lèi)重寫(xiě)
}
//(當(dāng)前類(lèi)LinkedHashMap實(shí)現(xiàn))
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);//頭插法
table[bucketIndex] = e;
e.addBefore(header);//實(shí)現(xiàn)第二功能,使數(shù)據(jù)實(shí)現(xiàn)插入有序;
size++;
}
//(當(dāng)前類(lèi)LinkedHashMap實(shí)現(xiàn)) ,addBefore為子類(lèi)Entry內(nèi)部類(lèi)中的方法,
//Entry多了兩個(gè)屬性,before(前驅(qū)),after(后驅(qū))
private void addBefore(Entry<K,V> existingEntry) {
//使header實(shí)現(xiàn)插入有序,header所處鏈表實(shí)質(zhì)上為一個(gè)循環(huán)的雙向鏈表;
//將header.after理解為頭結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn),將header.before理解為尾結(jié)點(diǎn);新結(jié)點(diǎn)插入位置為尾插
after = existingEntry;//新的尾結(jié)點(diǎn)鏈接頭結(jié)點(diǎn)
before = existingEntry.before;//新的尾結(jié)點(diǎn)的前驅(qū)鏈接舊的尾結(jié)點(diǎn)
before.after = this;//舊的尾結(jié)點(diǎn)的下一結(jié)點(diǎn)鏈接新的尾結(jié)點(diǎn)
after.before = this;//新的尾結(jié)點(diǎn)的下一結(jié)點(diǎn)的前驅(qū)鏈接新的尾結(jié)點(diǎn)
//當(dāng)然這句代碼可以改為existingEntry.before=this;//頭結(jié)點(diǎn)的前驅(qū)鏈接新的尾結(jié)點(diǎn)
}
//根據(jù)put操作和get操作,并且根據(jù)當(dāng)前集合是插入有序還是訪問(wèn)有序,進(jìn)行操作;
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) { //訪問(wèn)有序,刪除原節(jié)點(diǎn),并將新節(jié)點(diǎn)添加到最后;
lm.modCount++;
remove();//刪除當(dāng)前節(jié)點(diǎn)
addBefore(lm.header);//在末尾添加被刪除節(jié)點(diǎn)
}
}HashMap與LinkedHashMap的不同的點(diǎn):
LinkedHashMap可以保證插入有序或者訪問(wèn)有序
內(nèi)部類(lèi)Entry多了before / after
實(shí)現(xiàn)兩種數(shù)據(jù)結(jié)構(gòu),HashMap只實(shí)現(xiàn)數(shù)組+鏈表的數(shù)據(jù)結(jié)構(gòu),LinkedHashMap實(shí)現(xiàn)數(shù)組加鏈表和雙向鏈表環(huán)的數(shù)據(jù)結(jié)構(gòu)。
LinkedHashMap繼承自HashMap。兩者數(shù)組加鏈表得數(shù)據(jù)結(jié)構(gòu),功能差不多。但是在rehash時(shí),LinkedHashMap直接使用鏈表環(huán)進(jìn)行hash。這樣可以保證鏈表環(huán)相對(duì)不變。
到此這篇關(guān)于Java中的LinkedHashMap源碼詳解的文章就介紹到這了,更多相關(guān)LinkedHashMap源碼詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 遍歷Map及Map轉(zhuǎn)化為二維數(shù)組的實(shí)例
這篇文章主要介紹了java 遍歷Map及Map轉(zhuǎn)化為二維數(shù)組的實(shí)例的相關(guān)資料,希望通過(guò)本文能幫助到大家,實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08
springboot?整合?dubbo?的實(shí)現(xiàn)組聚合詳情
這篇文章主要介紹了springboot整合dubbo的實(shí)現(xiàn)組聚合詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07
mybatis中mapper.xml文件的常用屬性及標(biāo)簽講解
這篇文章主要介紹了mybatis中mapper.xml文件的常用屬性及標(biāo)簽講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Mybatis-Plus實(shí)現(xiàn)用戶ID自增出現(xiàn)的問(wèn)題解決
項(xiàng)目基于 SpringBoot + MybatisPlus 3.5.2 使用數(shù)據(jù)庫(kù)自增ID時(shí), 出現(xiàn)重復(fù)鍵的問(wèn)題,本文就來(lái)介紹一下解決方法,感興趣的可以了解一下2023-09-09
Java中父類(lèi)強(qiáng)制轉(zhuǎn)換子類(lèi)問(wèn)題解決
在Java編程中繼承是基礎(chǔ),但父類(lèi)轉(zhuǎn)換為子類(lèi)時(shí)需謹(jǐn)慎,正確的轉(zhuǎn)換要求父類(lèi)引用實(shí)際指向子類(lèi)對(duì)象,錯(cuò)誤轉(zhuǎn)換可能引發(fā)ClassCastException,解決方案包括使用instanceof關(guān)鍵字檢查、利用泛型避免轉(zhuǎn)換、以及通過(guò)多態(tài)性避免直接轉(zhuǎn)換,感興趣的可以了解一下2024-11-11
實(shí)例詳解Java中如何對(duì)方法進(jìn)行調(diào)用
這篇文章主要介紹了實(shí)例詳解Java中如何對(duì)方法進(jìn)行調(diào)用,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10
通過(guò)Java讀取xml文件內(nèi)容過(guò)程解析
這篇文章主要介紹了通過(guò)Java讀取xml文件內(nèi)容過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
idea打開(kāi)運(yùn)行配置java?web項(xiàng)目的全過(guò)程
這篇文章主要給大家介紹了關(guān)于idea打開(kāi)運(yùn)行配置java?web項(xiàng)目的相關(guān)資料,有些時(shí)候我們用IDEA跑之前用eclipse中運(yùn)行的項(xiàng)目的時(shí)候,總是不止所措,要不就是只展示html,要不就是不能部署成功,需要的朋友可以參考下2023-08-08

