本節將分析hashmap的增刪改查;
首先來說hashmap的插入流程:
1.計算下標 2.何時擴容 3.何時鍊錶轉紅黑樹,具體如下:
(1) 首先進行雜湊值的擾動,獲取新的hash值,(key==null) ? 0:(h=key.hashcode())^(h>>>16);
(2) 判斷tab是否為空或者長度為0,如果是則進行擴容操作。
if((tab=table) == null || (n=tab.length)==0) n=(tab=resize()).length;
(3).根據雜湊值計算下標,如果對應下標正好沒有存放資料,則直接插入即可
(4).判斷tab[i]是否為樹節點,否則向鍊錶中插入資料,是則向樹中插入節點
(5).如果鍊錶中插入節點的時候,鍊錶長度大於等於8,則需要把鍊錶轉換為紅黑樹,treeifybin(tab,hash)
(6).最後所有元素處理完成之後,判斷是否超過閾值;threshold,超過則擴容
(7).treeifybin,是乙個鍊錶轉樹的方法,但不是所有鍊錶長度為8都會轉化成樹,還需要判讀存放key值的陣列桶長度是否小於64,如果
小於則需要擴容,擴容後鍊錶上的資料會被拆分雜湊到相應的桶節點上,也就是把鍊錶長度縮短了。
1.8中,hashmap插入原始碼如下:
hashmap的擴容機制,首先hashmap是基於陣列+鍊錶和紅黑樹實現的,但用於存放key值的陣列的長度是固定的,由初始化的值所決定。擴容原始碼如下:
以上**看上去有點長,但是流程可以分為三步:
1.擴容時計算出新的newcap,newthr,這是兩個單詞的縮寫,乙個是capacity,乙個是threshold
2.newcap用於創新的資料桶 new node[newcap];
3.隨著擴容後,原來那些因為雜湊碰撞,存放成煉表和紅黑樹的元素,都需要進行拆分存放到新的位置中去。
在jdk1.8中由於要優化鍊錶過長所帶來的效能消耗,所以引入了紅黑樹這個資料結構,讓查詢資料的o(n)去逼近o(logn),但是紅黑樹的引入是
陣列的容量到達64,且鍊錶長度到達8的時候才會有。
總結下煉表轉紅黑樹的知識點:
1.鍊錶轉化為樹有兩點:鍊錶長度大於等於8,桶的容量大於64,否則只是擴容,不會樹化。
2.鍊錶樹化的過程中,是先由鍊錶轉化為樹節點,此時的樹可能還不是一顆平衡樹,同時在樹轉化的過程會記錄鍊錶的順序,t1.next=p,這主要方便後續樹
轉鍊錶和拆分更加方便
3.鍊錶轉化成樹之後,在進行紅黑樹的轉化。其中紅黑樹的轉化需要染色和旋轉,以及比對大小。在比對元素的大小中,有乙個比較有意思的方法,tiebreakorder
主要是因為hashmap沒有像treemap那樣本身就有comparator的實現。
紅黑樹轉成鍊錶其實很簡單,直接把treenode轉化為node即可,因為記錄裡鍊錶關係,所以替換過程很容易。所以好的資料結構可以讓操作變得更加容易。
原始碼如下:
hashmap查詢元素的流程如下:
hashmap的刪除操作也比較簡單,主要找到元素位置,對應鍊錶的元素刪除操作和紅黑樹元素的刪除操作。具體不展開敘述
hashmap的遍歷是日常使用中比較多的場景,主要有keyset和entryset兩種使用方法。
HashMap原始碼分析
public hashmap int initialcapacity,float loadfactor 2 接下來是重要的put方法,put方法用於將鍵值對儲存到map中,讓我們來具體分析一下。public v put k key,v value if key null 若key為null,則將va...
HashMap 原始碼分析
1 getentry object key 方法 final entrygetentry object key return null 根據key的hash值計算出索引,得到table中的位置,然後遍歷table處的鍊錶 for entrye table indexfor hash,table.le...
HashMap原始碼分析
public v put k key,v value if key null return putfornullkey value int hash hash key int i indexfor hash,table.length for entrye table i e null e e.nex...