HashMap中的hash算法中的幾個疑問

HashMap中哈希算法的關鍵代碼

//從新計算哈希值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//key若是是null 新hashcode是0 不然 計算新的hashcode
}
//計算數組槽位
 (n - 1) & hash

HashMap的細節咱們不談,只看這個哈希算法的細節(h = key.hashCode()) ^ (h >>> 16)算法

^按位異或運算,只要位不一樣結果爲1,否則結果爲0;
>>> 無符號右移:右邊補0數組

爲何要無符號右移16位後作異或運算

根據上面的說明咱們作一個簡單演練性能

將h無符號右移16爲至關於將高區16位移動到了低區的16位,再與原hashcode作異或運算,能夠將高低位二進制特徵混合起來spa

從上文可知高區的16位與原hashcode相比沒有發生變化,低區的16位發生了變化code

咱們可知經過上面(h = key.hashCode()) ^ (h >>> 16)進行運算能夠把高區與低區的二進制特徵混合到低區,那麼爲何要這麼作呢?blog

咱們都知道從新計算出的新哈希值在後面將會參與hashmap中數組槽位的計算,計算公式:(n - 1) & hash,假如這時數組槽位有16個,則槽位計算以下:hash

仔細觀察上文不難發現,高區的16位頗有可能會被數組槽位數的二進制碼鎖屏蔽,若是咱們不作剛纔移位異或運算,那麼在計算槽位時將丟失高區特徵class

也許你可能會說,即便丟失了高區特徵不一樣hashcode也能夠計算出不一樣的槽位來,可是細想當兩個哈希碼很接近時,那麼這高區的一點點差別就可能致使一次哈希碰撞,因此這也是將性能作到極致的一種體現效率

使用異或運算的緣由

 異或運算能更好的保留各部分的特徵,若是採用&運算計算出來的值會向1靠攏,採用|運算計算出來的值會向0靠攏hashmap

爲何槽位數必須使用2^n

一、爲了讓哈希後的結果更加均勻

這個緣由咱們繼續用上面的例子來講明

假如槽位數不是16,而是17,則槽位計算公式變成:(17 - 1) & hash

從上文能夠看出,計算結果將會大大趨同,hashcode參加&運算後被更多位的0屏蔽,計算結果只剩下兩種0和16,這對於hashmap來講是一種災難

二、能夠經過位運算e.hash & (newCap - 1)來計算,a % (2^n) 等價於 a & (2^n - 1)  ,位運算的運算效率高於算術運算,緣由是算術運算仍是會被轉化爲位運算

 

 

 

說了這麼多點,上面提到的全部問題,最終目的仍是爲了讓哈希後的結果更均勻的分部,減小哈希碰撞,提高hashmap的運行效率

相關文章
相關標籤/搜索