首先说下hashmap的实现基本逻辑
1.根据key值算出hashcode
2.用hash算法转换第一步的hashcode,得hash值
3.将第二步的hash值对内部数组长度进行取模,得到落点p
44.把value放入这个格子
从这些步骤可以看出,hash算法好不好直接决定了落点能否均匀分布。
hashMap是键值对的集合,比如key=“Hello”,value=“Hello”。
这个Hello拥有自己的hashCode,用如下代码可以看出来。
String key = "Hello";
System.out.println(key.hashCode());
hashCode对应10进制为69609650,二进制为100001001100010100010110010。
hashmap内部维护了一个数组,默认长度为16,当你存入一个键值对,就需要把key换算成这个数组的下标,再把value存进去。
也就是说,你得把 100001001100010100010110010 这个玩意变成 0-15之间的一个数字。
Jdk的做法就是取模,取模是计算机的说法,数学的讲法就是取余数。
所以,你可以直接这么写:
String key = "Hello";int hashCode = key.hashCode();int index = hashCode % 16;
System.out.println(index);
没问题,得到数字2,这就是最终的落点p。
取模操作有个装逼写法,据说效率更高:
int index = hashCode & (16-1);
别问为什么,问就是jdk源码就这么写的,俺也不知道,也不敢问。
a%b=a&(b-1) b为2的整次幂
15的二进制是1110,按位与就变成了
最终得到的就是0010,根据1248大法,立刻能口算出就是2(10进制)。
但是呢,这种hash算法是不太好的,我们直接取了hashCode,可以说没有算法在里面。
看下jdk1.8是怎么做的?
public static int hash(int a) {
return a ^ (a >>> 16);
}
哦,原来是先把hashCode右移16位,让高位移到低位,再和原来的异或一下。这样低位区也有高位区的特征。
100001001100010100010110010 -> 00000000000000001000010011
最终得到的结果是4,就不一样了。
总结:hash算法的目的就是让hashCode换算之后能够均匀分布在数组中,减少Hash冲突的产生。