HashMap原始碼分析 (JDK1 8

2021-08-19 19:16:40 字數 3730 閱讀 8241

首先,hashmap儲存結構類似於位桶,總體結構是

位桶+鍊錶+紅黑樹,

這與之前版本的實現有所改進。

常量域

預設容量16

static final int default_initial_capacity = 1 << 4;

最大容量2的30次方

static final int maximum_capacity = 1 << 30;

預設載入因子

static final float default_load_factor = 0.75f;

這是鍊錶轉換為紅黑樹的門檻值,即對於乙個桶中的節點大於8時,就改為紅黑樹儲存

static final int treeify_threshold = 8;

和上面是逆過程,紅黑樹中節點少於6時,改為鍊錶

static final int untreeify_threshold = 6;

table中最少要有64個節點才會轉換為紅黑樹

static final int min_treeify_capacity = 64;

hashmap的成員變數

node table;//儲存key-value健值對的陣列,在第一次插入時初始化

set> entryset

int size;//hashmap中key-value健值對的數量

int modcount;//修改次數,在有效的put和remove操作中會自增1,主要用在迭代器中

final nodenextnode() while (index < t.length && (next = t[index++]) == null);

}return e;

}即迭代過程中,如果執行了put或remove操作,導致modcount與開始迭代前expectedmodcount的值不一樣,會丟擲異常

int threshold;//門檻值,大於這個值時將會進行resize操作

float loadfactor//載入因子,預設0.75

1.構造方法

hashmap共有4個構造方法

public hashmap()

public hashmap(int initialcapacity)

public hashmap(int initialcapacity, float loadfactor)

public hashmap(map<? extends k, ? extends v> m)

這裡說下第三個

public hashmap(int initialcapacity, float loadfactor)

中的tablesizefor方法

static final int tablesizefor(int cap)

對於引數cap,它將返回比cap大且最小的2的整數次方對,例如10,它返回16。

演算法原理即是每次向右移動2的0次方、1次方。。。然後與n相或,這系列計算後會得到從n的二進位制數從左到右不為0的位起,將後面的位全部變為1,即n=10 =00001010,會變為00001111=15,在返回時+1變為16

2.put的方法

public v put(k key, v value)

轉到 putval方法,它是final的

//引數意義分別為

//hash key的hash值

//key 健值

//onlyifabsent 如果為true不改變已經存在的舊值

//evict

final v putval(int hash, k key, v value, boolean onlyifabsent,

boolean evict)

//此處與前面一樣

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

break;

p = e;}}

//e != null說明插入的值已存在

v oldvalue = e.value;

if (!onlyifabsent || oldvalue == null)

e.value = value;

//這方法是為 linkedhashmap準備的

afternodeaccess(e);

return oldvalue;}}

//修改次數+1

++modcount;

//size>門檻值了,重新計算容量

if (++size > threshold)

resize();

afternodeinsertion(evict);

return null;

}

再來看看treeifybin方法

final void treeifybin(node tab, int hash) 

tl = p;

} while ((e = e.next) != null);

if ((tab[index] = hd) != null)

hd.treeify(tab);

}}

這裡面用到了min_treeify_capacity常量,也就是說如果健值對個數少於64它依然不會轉化為紅黑樹,它會呼叫了resize方法就行擴容,擴容後健值對會分散,即達到乙個鍊錶中的健值對個數不大於8.

再看resize方法

這之前我們看看什麼情況會進行reszie操作,即擴容操作

(1)前面所講的紅黑樹轉換時,擴容目的是為分散健值對,如何分散請看下面

(2)首次進行put的操作時,會進行初始操作,各個成員變數會賦預設值

(3)put方法中,當此時size>threshold(容量上限)時

final node resize() 

//門檻值變為舊的兩倍

else if ((newcap = oldcap << 1) < maximum_capacity &&

oldcap >= default_initial_capacity)

newthr = oldthr << 1; // double threshold

}else if (oldthr > 0) // initial capacity was placed in threshold

newcap = oldthr;

//初始化

else

if (newthr == 0)

threshold = newthr;

@suppresswarnings()

node newtab = (node)new node[newcap];

table = newtab;

if (oldtab != null)

//位置為原來index+oldcap

else

} while ((e = next) != null);

if (lotail != null)

if (hitail != null) }}

}}

return newtab;

}

分析HashMap 的 JDK 原始碼

緣由 今天好友拿著下面的 問我為什麼 map.entry 這個介面沒有實現 getkey 和 getvalue 方法,卻可以使用,由此,開啟了一番查閱 jdk 原始碼的旅途 map map new hashmap map.put 1,張三 map.put 2,李四 map.put 3,王五 map....

hashmap原始碼分析jdk8

最近看了下jdk8的hashmap原始碼,相比於7,在儲存結構上有了些改變。1.在jdk8之前,hashmap的儲存結構是陣列 鍊錶的形式,那麼這種方式隨著鍊錶的長度增加,效率也凸顯出來。所以在jdk8中這塊做了優化,當鍊表超過一定長度時轉化為紅黑樹來解決這個問題,下面用流程圖畫出hashmap 的...

HashMap原始碼分析 JDK1 8

陣列 鍊錶 紅黑樹 陣列儲存元素,當有衝突時,就在該陣列位置形成乙個鍊錶,儲存衝突的元素,當鍊表元素個數大於閾值時,鍊錶就轉換為紅黑樹。transient node table transient int size int threshold final float loadfactor 預設初始容...