當(dāng)我們put的時(shí)候,首先計(jì)算 key的hash值,這里調(diào)用了 hash方法,hash方法實(shí)際是讓key.hashCode()與key.hashCode()>>>16進(jìn)行異或操作,高16bit補(bǔ)0,一個(gè)數(shù)和0異或不變,所以 hash 函數(shù)大概的作用就是:高16bit不變,低16bit和高16bit做了一個(gè)異或,目的是減少碰撞。
按照函數(shù)注釋,因?yàn)閎ucket數(shù)組大小是2的冪,計(jì)算下標(biāo)index = (table.length - 1) & hash,如果不做 hash 處理,相當(dāng)于散列生效的只有幾個(gè)低 bit 位,為了減少散列的碰撞,設(shè)計(jì)者綜合考慮了速度、作用、質(zhì)量之后,使用高16bit和低16bit異或來簡單處理減少碰撞,而且JDK8中用了復(fù)雜度 O(logn)的樹結(jié)構(gòu)來提升碰撞下的性能。
putVal方法執(zhí)行流程圖
1. 判斷鍵值對數(shù)組table[i]是否為空或?yàn)閚ull,否則執(zhí)行resize()進(jìn)行擴(kuò)容;
2. 根據(jù)鍵值key計(jì)算hash值得到插入的數(shù)組索引i,如果table[i]==null,直接新建節(jié)點(diǎn)添加,轉(zhuǎn)向⑥,如果table[i]不為空,轉(zhuǎn)向③;
3. 判斷table[i]的首個(gè)元素是否和key一樣,如果相同直接覆蓋value,否則轉(zhuǎn)向④,這里的相同指的是hashCode以及equals;
4. 判斷table[i] 是否為treeNode,即table[i] 是否是紅黑樹,如果是紅黑樹,則直接在樹中插入鍵值對,否則轉(zhuǎn)向5;
5. 遍歷table[i],判斷鏈表長度是否大于8,大于8的話把鏈表轉(zhuǎn)換為紅黑樹,在紅黑樹中執(zhí)行插入操作,否則進(jìn)行鏈表的插入操作;遍歷過程中若發(fā)現(xiàn)key已經(jīng)存在直接覆蓋value即可;
6. 插入成功后,判斷實(shí)際存在的鍵值對數(shù)量size是否超多了最大容量threshold,如果超過,進(jìn)行擴(kuò)容。