1. 程式人生 > >HashMap之擾動函數

HashMap之擾動函數

修改 代碼 pan div 進制 img 還要 key 做了

JDK8對擾動函數的修改,只進行了一次移位(又移16bit),再和key.hashCode()做異或,如圖

1 //JDK8中的散列值優化函數
2     static final int hash(Object key){
3         int h;
4         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
5     }

HashMap擴容之前的數組初始大小才16,所以這個散列值是不能直接拿來用的。用之前還要先做對數組的長度取模運算,得到的余數才能用來訪問數組下標。源碼中模運算是在這個indexFor( )函數裏完成的。

bucketIndex = indexFor(int h, table.length);

其中IndexFor代碼

1 static int indexFor(int h, int length){
2     return h & (length - 1);
3 }

這也正好解釋了為什麽HashMap的數組長度要取2的整次冪。因為這樣(數組長度-1)正好相當於一個“低位掩碼”。“與”操作的結果就是散列值的高位全部歸零,只保留低位值,用來做數組下標訪問。以初始長度16為例,16-1=15。2進制表示是00000000 00000000 00001111。和某散列值做“與”操作如下,結果就是截取了最低的四位值。

技術分享

但這時候問題就來了,這樣就算我的散列值分布再松散,要是只取最後幾位的話,碰撞也會很嚴重。更要命的是如果散列本身做得不好,分布上成等差數列的漏洞,恰好使最後幾個低位呈現規律性重復,就無比蛋疼。

這時候“擾動函數”的價值就體現出來了,說到這裏大家應該猜出來了。看下面這個圖,

技術分享

右位移16位,正好是32bit的一半,自己的高半區和低半區做異或,就是為了混合原始哈希碼的高位和低位,以此來加大低位的隨機性。而且混合後的低位摻雜了高位的部分特征,這樣高位的信息也被變相保留下來。

JDK 7做了4次右移,估計是邊際效應的原因,JDK8就只做了一次右移。

HashMap之擾動函數