1. 程式人生 > >JDK1.7中HashTable的hash為什麼對素數求餘,而不像HashMap中一樣對2的N次方求餘?

JDK1.7中HashTable的hash為什麼對素數求餘,而不像HashMap中一樣對2的N次方求餘?

常用的hash函式是選一個數m取模(餘數),這個數在課本中推薦m是素數,但是經常見到選擇m=2^n,因為對2^n求餘數更快,並認為在key分佈均勻的情況下,key%m也是在[0,m-1]區間均勻分佈的。但實際上,key%m的分佈同m是有關的。

證明如下:key%m = key - xm,即key減掉m的某個倍數x,剩下比m小的部分就是key除以m的餘數。顯然,x等於key/m的整數部分,以floor(key/m)表示。假設key和m有公約數g,即key=ag, m=bg, 則 key - xm = key - floor(key/m)m = key - floor(a/b)m。由於0 <= a/b <= a,所以floor(a/b)只有a+1中取值可能,從而推匯出key%m也只有a+1中取值可能。a+1個球放在m個盒子裡面,顯然不可能做到均勻。

由此可知,一組均勻分佈的key,其中同m公約數為1的那部分,餘數後在[0,m-1]上還是均勻分佈的,但同m公約數不為1的那部分,餘數在[0, m-1]上就不是均勻分佈的了。把m選為素數,正是為了讓所有key同m的公約數都為1,從而保證餘數的均勻分佈,降低衝突率。

鑑於此,在HashTable中,初始化容量是11,是個素數,後面擴容時也是按照2N+1的方式進行擴容,確保擴容之後仍是素數。而HashMap中可能是基於執行效率的考慮,在運算上採用按位運算取代取模運算,因此要求容器的容量是2的N次方,這樣在按位運算時只要將hash值&(2^N-1)。