计算过程。以下代码称为“扰动函数”//java8中的hash值优化函数staticfinalinthash(Objectkey){inth;返回(键==空)?0:(h=key.hashCode())^(h>>>16);}理论上hash是一个int值,如果直接作为下标访问hashmap,考虑二进制32位,则value范围是-2147483648~2147483647。大约有40亿个key。只要哈希函数映射比较统一和松散,一般就很难发生碰撞。一个客观的问题:要存储一个长度为40亿的数组,服务器内存是放不下的。通常我们的HashMap默认长度是16,所以这个hashCode,(key.hashCode)不能直接使用。使用前先对数组长度做AND运算,得到的值可以用来访问数组下标。代码如下://n=hashmap的长度p=tab[i=(n-1)&hash])这里为什么要用n-1进行AND运算,这里单AND是一个“低位掩码”",以默认长度16为例。与一个数进行AND运算,结果大小<16。如下:100000000010000000001001&000000000000000000001111-------------------------------000000000000000000001001//高位全部置0,此时只保留最后四位会有问题。如果hash值本身分布比较松散,只要取到后几位,碰撞就会很严重。另外,如果哈希本身没有做好,分布就会成为等差数列的漏洞,最后几位可能会出现规律的重复。这时候“扰动函数”的价值就体现出来了。如下图:hash函数中有这么一段代码:(h=key.hashCode())^(h>>>16)右移16位,正好是32bit的一半,用其自身的高半区异或,就是将原哈希码的高位和低位混合,增加低位的随机性。并且混合的低位掺杂了高位的一些特征,从而变相的保留了高位的信息。事实上,根据开发经验,HashMap在大多数情况下使用时长度不会超过1000,所以提高低位的随机性可以改善hash冲突,提高程序性能。有兴趣的可以浏览PeterLawlay专栏的一个实验《An introduction to optimising a hashing strategy》:他随机选取了352个字符串,在哈希值没有冲突的前提下将低位屏蔽掉,取数组标记.结果表明,当hashmap数组长度为512时,即使用低位掩码选择低9位时,在没有扰动函数的情况下有103次碰撞,接近30%。使用扰动函数后,只有92次碰撞。碰撞减少了近10%。说明扰动函数确实有效。但是很明显,Java8认为扰动做一次就够了,如果做四次,边际效用可能不大,所以出于效率的考虑改成了一次。代码演示?importjava.lang.reflect.Field;importjava.util.HashMap;/***HashMap计算hashKey*
*演示:扰动函数**@seeHashMap#hash(Object)*/publicclassHashKeyTest{publicstaticvoidmain(String[]args)throwsNoSuchFieldException,IllegalAccessException{HashMap