查询已有链表的hashmap_源码分析系列1:HashMap源码分析(基于JDK1.8)
1.HashMap的底層實現圖示
如上圖所示:
HashMap底層是由? 數組+(鏈表)=(紅黑樹)?組成,每個存儲在HashMap中的鍵值對都存放在一個Node節點之中,其中包含了Key-Value之外,還包括hash值(key.hashCode()) ^ (h >>> 16)) 以及執行下一個節點的指針next。
2.HashMap源碼分析
2.1 重要常量
public?class?HashMap?extends?AbstractMap????implements?Map,?Cloneable,?Serializable?{????private?static?final?long?serialVersionUID?=?362498820763181265L;????static?final?int?DEFAULT_INITIAL_CAPACITY?=?1?<
static?final?int?MAXIMUM_CAPACITY?=?1?<[]?table;
transient?Set>?entrySet;
transient?int?size;
transient?int?modCount;
int?threshold;
final?float?loadFactor;
DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16
MAXIMUM_CAPACITY : HashMap的最大支持容量,2^30
DEFAULT_LOAD_FACTOR:HashMap的默認加載因子
TREEIFY_THRESHOLD:Bucket中鏈表長度大于該默認值,轉化為紅黑樹
UNTREEIFY_THRESHOLD:Bucket中紅黑樹存儲的Node小于該默認值,轉化為鏈表
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量。(當桶中Node的數量大到需要變紅黑樹時,若hash表容量小于MIN_TREEIFY_CAPACITY時,此時應執行resize擴容操作這個MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
table:存儲元素的數組,總是2的n次冪
entrySet:存儲具體元素的集
size:HashMap中存儲的鍵值對的數量
modCount:HashMap擴容和結構改變的次數。
threshold:擴容的臨界值,=容量*填充因子
loadFactor:填充因子
2.2 重要方法
1.獲取hash值? hashstatic?final?int?hash(Object?key)?{????????int?h;????????return?(key?==?null)???0?:?(h?=?key.hashCode())?^?(h?>>>?16);
}
hash方法用傳入的key的hashCode和hashCode無符號右移16位的結果,做異或運算后作為hash值返回。
注:之所以獲取hashCode后,還需要和右移16位的hashCode做異或運算,原因是:在根據hash值獲取鍵值對在bucket數組中的下標時,采用的算法是:index=h & (length-1),當數組的length較小時,只有低位能夠參與到“與”運算中,但是將hashCode右移16位再與本身做異或獲取到的hash,可以使高低位均能夠參與到后面的與運算中。
下面圖說明:
2.根據鍵值對數量獲取HashMap容量方法? ?tableSizeFor
static?final?int?tableSizeFor(int?cap)?{????????int?n?=?cap?-?1;
n?|=?n?>>>?1;
n?|=?n?>>>?2;
n?|=?n?>>>?4;
n?|=?n?>>>?8;
n?|=?n?>>>?16;????????return?(n?=?MAXIMUM_CAPACITY)???MAXIMUM_CAPACITY?:?n?+?1;
}
tabSizeFor方法,主要根據傳入的鍵值對容量,來返回大于容量的最小的二次冪數值。
算法如下:
將傳入的容量-1:至于這里為什么需要減1,是為了防止cap已經是2的冪。如果cap已經是2的冪, 又沒有執行這個減1操作,則執行完后面的幾條無符號右移操作之后,返回的capacity將是這個cap的2倍,各位可自行驗證:
假設原始n:? ? 0001? xxxx xxxx xxxx第一次右移1位+或運算:二進制序列出現至少兩個連續的1,如?0001?1xxx?xxxx?xxxx;第二次右移2位+或運算:二進制序列出現至少四個連續的1,如?0001?111x?xxxx?xxxx;
第三次右移4位+或運算:二進制序列出現至少八個連續的1,?如?0001?1111?1111?xxxx;
第四次右移8位+或運算:二進制序列至少出現16個連續的1,如?0001?1111?1111?1111;第五次右移16位+或運算:二進制序列至少出現32個連續的1,如?0001?1111?1111?1111;上述運算中,若出現右移后為0,則或運算得到的結果和原始值一致,則后續推導過程可以忽略。
此時可以保證,原始序列從包含1的最高位,到最低位,全部都變成了1.
最后+1,返回的結果就是大于原值的最小二次冪數。
3.插入方法? ?putVal
1??final?V?putVal(int?hash,?K?key,?V?value,?boolean?onlyIfAbsent,?2????????????????????boolean?evict)?{?3?????????????????????4?????????Node[]?tab;????//存儲Node節點的數組tab?5?????????Node?p;?????????//單個Node節點p?6?????????int?n,?i;????????????//HashMap的容量n?7?????????//初始化數組桶table?8?????????if?((tab?=?table)?==?null?||?(n?=?tab.length)?==?0)?9?????????????n?=?(tab?=?resize()).length;10?????????//如果數組桶中不包含要插入的元素,將新鍵值對作為新Node存入數組,11?????????if?((p?=?tab[i?=?(n?-?1)?&?hash])?==?null)????//此處p初始化,p和需要存儲的鍵值對下標相同且P是鏈表的第一個元素12?????????????tab[i]?=?newNode(hash,?key,?value,?null);13?????????????14?????????//桶中包含要插入的元素15?????????else?{16?????????????Node?e;?K?k;17?????????????//如果key和鏈表第一個元素p的key相等18?????????????if?(p.hash?==?hash?&&19?????????????????((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))20?????????????//則將e指向該鍵值對21?????????????????e?=?p;22?????????????//若p是TreeNode類型,則使用紅黑樹的方法插入到樹中23?????????????else?if?(p?instanceof?TreeNode)24?????????????????e?=?((TreeNode)p).putTreeVal(this,?tab,?hash,?key,?value);25?????????????//else:鍵值對的引用不在鏈表的第一個節點,此時需要遍歷鏈表????26?????????????else?{27?????????????????for?(int?binCount?=?0;?;?++binCount)?{28?????????????????????//將e指向p的下一個元素,直到其next為null時,將鍵值對作為新Node放到p的尾部或樹中。29?????????????????????if?((e?=?p.next)?==?null)?{30?????????????????????????p.next?=?newNode(hash,?key,?value,?null);31?????????????????????????//如果遍歷鏈表的長度大于等于8,則變成樹32?????????????????????????if?(binCount?>=?TREEIFY_THRESHOLD?-?1)?//?-1?for?1st33?????????????????????????????treeifyBin(tab,?hash);34?????????????????????????break;???//新元素已追加到鏈表尾部或樹中,退出遍歷35?????????????????????}36?????????????????????//在沖突鏈表中找到另一個具有相同key值的節點,退出遍歷37?????????????????????if?(e.hash?==?hash?&&38?????????????????????????((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))39?????????????????????????break;40?????????????????????????41?????????????????????//將e指向p,便于下次遍歷e?=?p.next42?????????????????????p?=?e;43?????????????????}44?????????????????//上述for循環執行完畢后,e要么指向了存儲的新節點,要么是原來已有的元素,具有和新節點一樣key值45?????????????}46?????????????//當e非空時,說明e是原來HashMap中的元素,具有和新節點一樣的key值47?????????????if?(e?!=?null)?{?//?existing?mapping?for?key48?????????????????V?oldValue?=?e.value;49?????????????????if?(!onlyIfAbsent?||?oldValue?==?null)????//onlyIfAbsent?表示是否僅在?oldValue?為?null?的情況下更新鍵值對的值50?????????????????????e.value?=?value;51?????????????????//空實現,LinkedHashMap用52?????????????????afterNodeAccess(e);53?????????????????return?oldValue;54?????????????}55?????????}56?????????//HashMap結構更改,modCount+157?????????++modCount;58?????????//判斷是否需要擴容59?????????if?(++size?>?threshold)60?????????????resize();61?????????//空實現,LinkedHashMap用62?????????afterNodeInsertion(evict);63?????????return?null;64?????}
HashMap中進行存儲的入口方法是:put(K,V),但是核心方法是putVal方法,該方法一共有以下步驟:初始化數組桶
判斷數組桶中對應下標是否無元素存在,是,就直接存入
若數組桶中對應下標有元素存在,則開始遍歷,根據長度將元素存入鏈表尾部或樹中。
判斷是否需要擴容
4.擴容方法? ?resize
1?final?Node[]?resize()?{??2?????????Node[]?oldTab?=?table;??3?????????int?oldCap?=?(oldTab?==?null)???0?:?oldTab.length;????//原HashMap的容量??4?????????int?oldThr?=?threshold;????????????????????????????????//原HashMap的擴容臨界值??????????????????5?????????int?newCap,?newThr?=?0;??6?????????//case1:odlCap>0,說明桶數組已經初始化過??7?????????if?(oldCap?>?0)?{??8?????????????//原HashMap的越界檢查??9?????????????if?(oldCap?>=?MAXIMUM_CAPACITY)?{?10?????????????????threshold?=?Integer.MAX_VALUE;?11?????????????????return?oldTab;?12?????????????}?13?????????????//容量擴大一倍后的越界檢查?14?????????????else?if?((newCap?=?oldCap?<=?DEFAULT_INITIAL_CAPACITY)?16?????????????????newThr?=?oldThr?<0,桶數組尚未初始化,當調用帶初始化容量的構造函數時會發生該情況?19?????????else?if?(oldThr?>?0)?//?initial?capacity?was?placed?in?threshold?20?????????????//在前面HashMap的初始化中,將Initial?capcity暫存在threshold中?21?????????????newCap?=?oldThr;?//未初始化,則用Initial?capcity作為新的容量?22??????????????23?????????????//若oldThr?=?threshold?=?0,則說明未傳入初始化容量參數?24??????????????25?????????//case3:oldCap=0?&&?oldThr?=?0,當調用無參構造函數時會發生該情況,此時使用默認容量初始化?26?????????else?{???????????????//?zero?initial?threshold?signifies?using?defaults?27?????????????newCap?=?DEFAULT_INITIAL_CAPACITY;????//默認容量?28?????????????newThr?=?(int)(DEFAULT_LOAD_FACTOR?*?DEFAULT_INITIAL_CAPACITY);????//默認擴容臨界值?29?????????}?30??????????31?????????//?當newThr?為?0?時,閾值按照計算公式進行計算?32?????????if?(newThr?==?0)?{?33?????????????float?ft?=?(float)newCap?*?loadFactor;?34?????????????newThr?=?(newCap?[]?newTab?=?(Node[])new?Node[newCap];?46?????????table?=?newTab;?47?????????//若oldTab非空,則需要將原來桶數組的元素取出來放到新的桶數組中?48?????????if?(oldTab?!=?null)?{?49?????????????for?(int?j?=?0;?j??e;?51?????????????????if?((e?=?oldTab[j])?!=?null)?{?52?????????????????????oldTab[j]?=?null;????//將原桶數組的元素占用的空間釋放,便于GC?53?????????????????????if?(e.next?==?null)????
54?????????????????????????//若桶中元素的next為空,獲取index后直接將其放入新桶數組中?55?????????????????????????newTab[e.hash?&?(newCap?-?1)]?=?e;?56?????????????????????????//若桶中元素的next是樹節點?57?????????????????????else?if?(e?instanceof?TreeNode)?58?????????????????????????//采用樹的方式插入?59?????????????????????????((TreeNode)e).split(this,?newTab,?j,?oldCap);?60?????????????????????????//若桶中元素的next是鏈表節點?61?????????????????????else?{?//?preserve?order?62?????????????????????????Node?loHead?=?null,?loTail?=?null;?63?????????????????????????Node?hiHead?=?null,?hiTail?=?null;?64?????????????????????????Node?next;?65??????????????????????????66?????????????????????????/*遍歷原鏈表,按照原來的順序進行分組?67?????????????????????????*/?68??????????????????????????69??????????????????????????70?????????????????????????/*原始鏈表中的元素,在resize之后,其下標有兩種可能,一種是在原來下標處,另一種是原來下標+oldCap處?71?????????????????????????*舉例說明:??若原來的容量?-1后?只有n位,低位有n個1,去下標公式為:i?=?(oldCap?-?1)?&?hash,若hash值只有低n為有值,則與運算后獲得的值和?72?????????????????????????*擴容前是一樣的,若hash不止第n位有值,那采用與運算后,結果比原來剛好大oldCap。?下面有圖片示例)?73?????????????????????????*/?74
75??????????????????????????76?????????????????????????do?{?77?????????????????????????????next?=?e.next?78?????????????????????????????//若e.e.hash?&?oldCap?結果為0,則下標在新桶數組中不用改變,此時,將元素存放在loHead為首的鏈表中?79?????????????????????????????if?((e.hash?&?oldCap)?==?0)?{?80?????????????????????????????????if?(loTail?==?null)?81?????????????????????????????????????loHead?=?e;?82?????????????????????????????????else?83?????????????????????????????????????loTail.next?=?e;?84?????????????????????????????????loTail?=?e;?85?????????????????????????????}?86??????????????????????????????87?????????????????????????????//若e.e.hash?&?oldCap?結果不為0,則下標在新桶數組等于原下標+oldCap,此時,將元素存放在hiHead為首的鏈表中?88?????????????????????????????else?{?89?????????????????????????????????if?(hiTail?==?null)?90?????????????????????????????????????hiHead?=?e;?91?????????????????????????????????else?92?????????????????????????????????????hiTail.next?=?e;?93?????????????????????????????????hiTail?=?e;?94?????????????????????????????}?95?????????????????????????}?while?((e?=?next)?!=?null);?96??????????????????????????97??????????????????????????98??????????????????????????99?????????????????????????if?(loTail?!=?null)?{????????//loHead為首的鏈表中,下標不改變100?????????????????????????????loTail.next?=?null;101?????????????????????????????newTab[j]?=?loHead;102?????????????????????????}103?????????????????????????if?(hiTail?!=?null)?{????????//hiHead為首的鏈表中,下標=原下標+oldCap104?????????????????????????????hiTail.next?=?null;105?????????????????????????????newTab[j?+?oldCap]?=?hiHead;106?????????????????????????}107?????????????????????}108?????????????????}109?????????????}110?????????}111?????????return?newTab;????????
112?????}
上述代碼分析較長,總結如下:
1.獲取不同情況下的 新的容量 和 新的擴容臨界值
2.根據新容量創建新的桶數組tab。
3.根據節點類型,樹節點和鏈表節點分別采用對應方法放入新的桶數組
5.移除元素? remove
1??final?Node?removeNode(int?hash,?Object?key,?Object?value,?2????????????????????????????????boolean?matchValue,?boolean?movable)?{?3?????????Node[]?tab;?Node?p;?int?n,?index;?4??????????5?????????//通過hash值獲取下標,下標對應的節點p不為空?6?????????if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&?7?????????????(p?=?tab[index?=?(n?-?1)?&?hash])?!=?null)?{?8?????????????Node?node?=?null,?e;?K?k;?V?v;?9?????????????//若節點p的key和待移除的節點key相等10?????????????if?(p.hash?==?hash?&&11?????????????????((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))12?????????????????//則p就是待移除節點13?????????????????node?=?p;????//將p指向待移除節點14?????????????//p的key和待移除的節點key不相等,遍歷p作為頭的鏈表或者樹15?????????????else?if?((e?=?p.next)?!=?null)?{16?????????????????//若p是樹節點17?????????????????if?(p?instanceof?TreeNode)18?????????????????????//采用樹節點方式獲得要移除的節點19?????????????????????node?=?((TreeNode)p).getTreeNode(hash,?key);20?????????????????else?{//p是鏈表的頭節點21?????????????????????do?{22?????????????????????????//采用循環,當p.next不為空,比對它和傳入的key,直到找到相等的key23?????????????????????????if?(e.hash?==?hash?&&24?????????????????????????????((k?=?e.key)?==?key?||25??????????????????????????????(key?!=?null?&&?key.equals(k))))?{26?????????????????????????????//找到后,將節點指向node27?????????????????????????????node?=?e;????//將e指向待移除節點,此時相當于p.next就是待移除的節點node,可自行驗證循環便知28?????????????????????????????break;29?????????????????????????}30?????????????????????????p?=?e;??
31?????????????????????}?while?((e?=?e.next)?!=?null);32?????????????????}33?????????????}34?????????????//若node非空,傳入的matchValue參數為flase或?node的value等于傳入value35????????????if?(node?!=?null?&&?(!matchValue?||?(v?=?node.value)?==?value?||36??????????????????????????????????(value?!=?null?&&?value.equals(v))))?{37?????????????????//若node是樹節點38?????????????????if?(node?instanceof?TreeNode)39?????????????????????//采用樹節點的方式移除40?????????????????????((TreeNode)node).removeTreeNode(this,?tab,?movable);41?????????????????????//若待移除節點是鏈表頭,將其指向待移除元素的next,移除對node的引用42?????????????????else?if?(node?==?p)43?????????????????????tab[index]?=?node.next;
44?????????????????else//待移除元素是鏈表中的元素,此時其等于p.next45?????????????????????//將p.next指向node.next,移除了對node的引用46?????????????????????p.next?=?node.next;47?????????????????//增加結構修改計數器48?????????????????++modCount;49?????????????????//size-150?????????????????--size;51?????????????????//空實現,LinkedHashMap用52?????????????????afterNodeRemoval(node);53?????????????????54?????????????????//返回移除的節點node55?????????????????return?node;56?????????????}57?????????}58?????????return?null;59?????}
移除節點的入口方法是:?public V remove(Object key)? ,其核心方法是removeNode,主要做了以下幾個工作:通過用key獲取的hash,來獲取下標。
若下標對應處無元素,返回null。
若下標對應處有元素,判斷是樹或者鏈表,采用對應方法移除。
6.查找元素方法 get
1?????final?Node?getNode(int?hash,?Object?key)?{?2?????????Node[]?tab;?Node?first,?e;?int?n;?K?k;?3?????????//根據hash值,獲取對應下標的第一個元素first?4?????????if?((tab?=?table)?!=?null?&&?(n?=?tab.length)?>?0?&&?5?????????????(first?=?tab[(n?-?1)?&?hash])?!=?null)?{?6?????????????//如果first的key和待查詢的key相等,返回first?7?????????????if?(first.hash?==?hash?&&?//?always?check?first?node?8?????????????????((k?=?first.key)?==?key?||?(key?!=?null?&&?key.equals(k))))?9?????????????????return?first;10?????????????//若first不是待查詢的元素11?????????????if?((e?=?first.next)?!=?null)?{12?????????????????//若first是樹節點,采用樹節點的方式獲取13?????????????????if?(first?instanceof?TreeNode)14?????????????????????return?((TreeNode)first).getTreeNode(hash,?key);15?????????????????//first是鏈表節點頭,使用循環獲取16?????????????????do?{17?????????????????????if?(e.hash?==?hash?&&18?????????????????????????((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))19?????????????????????????return?e;20?????????????????}?while?((e?=?e.next)?!=?null);21?????????????}22?????????}23?????????return?null;24?????}
查詢元素的入口方法是:public V get(Object key),返回值是node的value,核心方法是getNode(int hash, Object key)。
2.3 構造方法
1.無參構造函數public?HashMap()?{????????this.loadFactor?=?DEFAULT_LOAD_FACTOR;?//?all?other?fields?defaulted
}
使用所有默認參數來構造一個HashMap,我們常用的就是這種。
2.給出容量的構造函數public?HashMap(int?initialCapacity)?{????????this(initialCapacity,?DEFAULT_LOAD_FACTOR);
}
此處調用了下面這個構造函數,將給出的容量傳入和默認負載因子。
3.給出容量和負載因子的構造函數
public?HashMap(int?initialCapacity,?float?loadFactor)?{
//容量越界檢查????????if?(initialCapacity?
initialCapacity);????????if?(initialCapacity?>?MAXIMUM_CAPACITY)
initialCapacity?=?MAXIMUM_CAPACITY;
//負載因子非負非空檢查????????if?(loadFactor?<=?0?||?Float.isNaN(loadFactor))????????????throw?new?IllegalArgumentException("Illegal?load?factor:?"?+
loadFactor);
this.loadFactor?=?loadFactor;
this.threshold?=?tableSizeFor(initialCapacity);??//此處將初始化的容量暫存到threshold中
}
4.用一個map來初始化的構造函數public?HashMap(Map?extends?K,???extends?V>?m)?{????????this.loadFactor?=?DEFAULT_LOAD_FACTOR;
putMapEntries(m,?false);
}
此處將map中所有元素放入HashMap進行初始化。
3.常見問題解答
3.1 HashMap的容量為什么必須是2的n次冪?
答:當容量是2的n次冪時,不同的key獲取在桶數組中的下標相同的概率減小,發生Hash碰撞幾率減少,元素分布更加均勻,見下圖。
結論:
1.由上述實例可以看出,當HashMap容量為2的n次冪的時候,length-1,可以使得在計算index的"&"運算過程中,hash值的對應位都能參與到計算;若HashMap容量不等于2的n次冪,leng-1后必然有一些位是等于0的,那么在計算index的"&"運算過程中,對應位的數字無論是0或者1,都未能參與到運算中。導致Hash沖突概率增大。
2.初次之外,若HashMap容量不為2的n次冪,無論Hash值如何變化,始終有一些下標值無法取得,因為"&"運算過程中,必然有一些位置結果始終為0,如實例所示,其最低位始終為0,因此下標 1(二進制0000 0001)、3(二進制0000 0011)、5(二進制0000 0101)、7(二進制0000 0111)等下標、永遠都獲取不了。造成了容量的浪費
3.2 HashMap的時間復雜度?
答:O(1)或者O(log(n))或O(n),分析如下:
根據第一節的內容可知,根據HashMap的數據結構,可能有以下三種情況:
1.無鏈表和紅黑樹,理想狀態。每個桶只有一個或0個元素,不存在Hash沖突,此時時間復雜度為O(1);但此時耗費的內存空間較大。
2.只有鏈表,此時因為需要循環鏈表來獲取元素,時間復雜度為O(n)
3.只有紅黑樹,此時時間復雜度為紅黑樹查找的時間復雜度,O(log(n)).
4.鏈表和紅黑樹均有,此時時間復雜度依據紅黑樹的層數和鏈表長度而定。為O(n)或者O(log(n)).
3.3 負載因子LoadFactor為何默認值為0.75。
當負載因子過大時,Hash沖突概率增加、讀寫的時間復雜度也大大增加,當負載因子過小時,Hash沖突概率較小,時間復雜度較低,但占用內存空間較大。
至于為什么默認值是0.75,這是一個基于時間和空間復雜度的綜合考慮的結果,可以參考這篇文章:HashMap的loadFactor為什么是0.75?
3.4 作為HasHMap的key有什么條件?
使用HashMap,若用int/String等作為key值,因為Integer類和String類都以及重寫了equals()和hashCode()方法.
但是如果key是自定義的類,就必須重寫hashcode()和equals()。理由如下:
//在插入元素中,根據hash值后,與length-1做&運算獲取下標???????//獲取hash
static?final?int?hash(Object?key)?{????????int?h;????????return?(key?==?null)???0?:?(h?=?key.hashCode())?^?(h?>>>?16);
}???????//獲取下標
p?=?tab[i?=?(n?-?1)?&?hash]????//用equals方法比對key值和節點的key值,來確認是否遍歷到所需元素
(key?!=?null?&&?key.equals(k)))
/*對比hash值,如果不重寫hashCode方法,那么采用Object類的默認的hash方法是獲取內存地址,此時即使兩個key對象相等,但其內存地址不可能相等,所以必須重寫hashCode方法。*//*equals方法若不重寫,采用的Object的equals方法,比對的是內存地址,如果不重寫,會造成兩個一樣的key值都插入,存在重復元素*/
//同理,在查找過程中,在第二節putVal方法中有分析,會用到hash值,以及用到key.equals方法,因此如果不重寫equals()和hashCode(),會造成雖然元素存在,但是因內存地址不一致,差找不到對應元素。
3.5 HashMap key允許為空嗎?,最多幾個?
答:允許但只允許一個key值為空。當key值為空時,其hash值為0,因此在桶數組中位置是0,即第一個元素。
那么為什么不能有兩個key值為null呢,原因是兩個key為null,是一樣的,后面put進去的(null,value2)會覆蓋先put進去的(null,value1)。驗證如下:
3.6 HashMap value允許為空嗎?最多幾個?
答:允許,可以有多個value為null,查看源碼可知,在putVal中沒有對value進行限制,驗證如下:
寫在最后:
1.本文中設計到數操作的都沒有詳細介紹,因為紅黑樹本身概念較為抽象復雜,打算下一篇文章中再來詳細分析一下,還有其他一些類似于“map.clear()、map.ContainsKey()”等等邏輯較為簡單的方法也未作贅述。
2.不得不感嘆一些設計Java集合類的大牛是真的牛,看似一個簡單的HashMap中、對于位運算、鏈表。紅黑樹的應用可謂是爐火純青,看起來都不能一目了然,設計時那更是天馬行空,匠心獨運。
總結
以上是生活随笔為你收集整理的查询已有链表的hashmap_源码分析系列1:HashMap源码分析(基于JDK1.8)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: fsimage文件丢失_Fsimage
- 下一篇: vba 跳出for循环_VBA简单入门0