下面是HashTable源碼中的put方法:git
注意上面註釋標註的地方:github
HashTable對於元素在哈希表中的座標算法是:算法
HashMap中哈希算法比HashTable中的稍微複雜一點。整體能夠分爲兩步:markdown
上面代碼中,首先是一個三目運算符,判斷key是否是等於null,等於null,則返回0做爲哈希值。不然,運算(h=key.hashCode()) ^ (h >>> 16),將key的哈希值的高位與低位異或的結果做爲低位,改成不變。優化
‘>>>’是無符號右移操做,高位補0.atom
爲何要這麼作呢?下面這一點講了事後咱們就明白了spa
一樣以put方法爲例.net
從最後一行咱們能夠看出,HashMap的哈希座標計算方法是: (n - 1) & hash,其中hash就是咱們第一點講的改進哈希碼。對象
舉例分析以下,假設key的原始哈希值是’1111 1111 1111 1111 1111 0000 1110 1010’blog
(ps:圖片來自:https://blog.csdn.net/john_520/article/details/57415084)
咱們注意到,在上面這種哈希表長度較小的狀況下,哈希碼只有低4位與表的長度進行了關聯性計算。這會形成哈希碼的不充分使用,從而更容易引發哈希衝突。爲了充分利用哈希碼的高位,HashMap經過(h=key.hashCode()) ^ (h >>> 16)運算,將高位與低位異或,使得即便在表長較小的狀況下,高位也能參與計算,使得衝突的機率減少了。
我在Stack Overflow上找到了一個解答:
意思是說:「規範的解決方法是將哈希值與表長取模,而這個方式((n - 1) & hash )武漢英語學校充分利用了HashMap的表長是2的整數次冪的事實,使用效率較高的位與運算(取模的高度優化)來替代昂貴的取模運算。」
實際上這兩種方式的實質是同樣的,它只是利用了這樣一個事實:在n是2的冪的狀況下,(n - 1) & hash 等同於 hash%h。
咱們以10爲例,演示以下:
能夠看到,兩種方式的計算結果是相同的(這不是巧合),這其實是一個數學規律。
在源碼中有這樣一個方法:
這個方法的做用如註釋所說,是求大於cap的最小2的整數次冪。在用戶指定的初始容量不是2的冪時,HashMap會調用該方法將其變得符合要求。此後,每次擴容時是這樣的:
直接使用oldCap<<1來將容量擴大爲原來的2倍,即乘以21。
我對JDK源碼的閱讀和中文註釋都已經同步到Github,歡迎英語閱讀困難戶前往查看:)
連接是:https://github.com/Dodozhou/JDK,喜歡的話別忘了star哦。