Java中的LinkedHashMap源碼詳解
LinkedHashMap
特點(diǎn):
底層數(shù)據(jù)結(jié)構(gòu):
- 數(shù)組加鏈表用來存儲(chǔ)數(shù)據(jù);
- header雙向鏈表用來實(shí)現(xiàn)數(shù)據(jù)插入有序或者訪問有序;
繼承關(guān)系:
public class LinkedHashMap<K,V> extends HashMap<K,V> //繼承了HashMap implements Map<K,V>//實(shí)現(xiàn)了Map接口 {
- 默認(rèn)數(shù)組大?。?6 ==>繼承父類
- loadFactor(默認(rèn)加載因子):0.75 ==>繼承父類
基本屬性:下面為LinkedHashMap特有,別的屬性全部繼承HashMap;
private transient Entry<K,V> header; //頭結(jié)點(diǎn) private final boolean accessOrder;//順序性; true(訪問有序); false(插入有序)
header如何初始化:header初始化需要調(diào)用重寫后的 init()方法,創(chuàng)建一個(gè)不存儲(chǔ)數(shù)據(jù)的entry實(shí)體,而init方法是在父類的構(gòu)造函數(shù)中被調(diào)用,子類的初始化都會(huì)調(diào)用父類的構(gòu)造函數(shù),從而實(shí)現(xiàn)了header的初始化;
//子類重寫方法; @Override void init() { header = new Entry<>(-1, null, null, null); //hash值為-1; header.before = header.after = header; } //父類構(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起作用,用來初始化header; }
構(gòu)造函數(shù):均是調(diào)用父類對(duì)應(yīng)的構(gòu)造函數(shù)
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor);//調(diào)用父類的構(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; }
增長方式:繼承父類,2*table.length; CRUD(增刪改查): put: 調(diào)用的是父類的put方法,但是對(duì)put方法中一些相關(guān)函數(shù)進(jìn)行重寫;
// (父類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); //通過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);//此方法在子類LinkedHashMap重寫,發(fā)揮作用; //針對(duì)LinkedHashMap,確定是插入有序,還是插入有序; //如果是訪問有序,將原節(jié)點(diǎn)刪除,并添加到header最后 return oldValue; } } modCount++; addEntry(hash, key, value, i);//現(xiàn)有集合中沒找到key,那么創(chuàng)建一個(gè)新的entry實(shí)體 return null; } //(當(dāng)前類LinkedHashMap實(shí)現(xiàn)) void addEntry(int hash, K key, V value, int bucketIndex) { //目前此函數(shù)并沒有看出與父類的區(qū)別 super.addEntry(hash, key, value, bucketIndex);//調(diào)用父類創(chuàng)建entry實(shí)體 // Remove eldest entry if instructed Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) {//總返回false;這句等于無效代碼,留作后用; removeEntryForKey(eldest.key); } } //(父類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í)體,子類重寫 } //(當(dāng)前類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)前類LinkedHashMap實(shí)現(xiàn)) ,addBefore為子類Entry內(nè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)前集合是插入有序還是訪問有序,進(jìn)行操作; void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { //訪問有序,刪除原節(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可以保證插入有序或者訪問有序
內(nè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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot報(bào)錯(cuò)java.lang.NullPointerException: null問題
這篇文章主要介紹了Springboot報(bào)錯(cuò)java.lang.NullPointerException: null問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11springboot @validated List校驗(yàn)失效問題
這篇文章主要介紹了springboot @validated List校驗(yàn)失效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07SpringBoot默認(rèn)包掃描機(jī)制及@ComponentScan指定掃描路徑詳解
這篇文章主要介紹了SpringBoot默認(rèn)包掃描機(jī)制及@ComponentScan指定掃描路徑詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11SpringBoot+Email發(fā)送郵件的實(shí)現(xiàn)示例
Spring?Boot提供了簡單而強(qiáng)大的郵件發(fā)送功能,本文主要介紹了SpringBoot+Email發(fā)送郵件的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03MyBatis-Plus 分頁插件配置的兩種方式實(shí)現(xiàn)
本文主要介紹了MyBatis-Plus 分頁插件配置的兩種方式實(shí)現(xiàn),包括使用PaginationInterceptor和MybatisPlusInterceptor兩種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03Java實(shí)現(xiàn)雪花算法的原理和實(shí)戰(zhàn)教程
這篇文章主要介紹了Java實(shí)現(xiàn)雪花算法的原理和實(shí)戰(zhàn)教程,本文通過語言表述和代碼的實(shí)現(xiàn)講解了該項(xiàng)算法,,需要的朋友可以參考下2021-06-06