首先说下hashmap的实现基本逻辑

  1. 根据key值算出hashcode
  2. 用hash算法转换第一步的hashcode,得hash值
  3. 将第二步的hash值对内部数组长度进行取模,得到落点p
  4. 把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冲突的产生。

posted on 2022-05-02 08:00  剽悍一小兔  阅读(3)  评论(0编辑  收藏  举报  来源