HashMap初始化数组原理,为什么是2的N次方?

Jdk1.8初始化hashMap容量的算法

static final int tableSizeFor(int cap) {
  // 先减1,避免传进来的本来就是2的n次幂,导致算出来多了一次幂,比如传16会得到32,实际上16即可
int n = cap - 1;
  // 低位全部变1, int4字节*1个字节8位=32位
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
  //再加1获取2的N次幂作为容量
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

举例,Map map = new hashMap(10)
10-1=9
对应二进制    0000 1001

右移1位     0000 0100
或运算     0000 1101

结果右移2位   0000 0011
或运算 0000 1111

结果右移4位   0000 0000
或运算 0000 1111
```

不断右移然后或运算后,最后低位全部变1
加1后 得到   0001 0000 得到16为最接近10的二的次方数
hashmap数组容量2的N次幂的好处是:

查看put方法计算元素数组下标核心代码
i = (n - 1) & hash

由于N为2的N次幂,N减1后得到高位为0,低位全为1的二进制
进行与运算后,不可能得到比N大的数
举例:

n= 16,则n-1的二进制为0000 1111
假设hashcode为**** ****
与运算后,必定得到 0000 ****,所以不可能大于16,范围为00000000-00001111,即不存在数组越界问题

这里又会引申出一个问题,这样的话,hashCode的高位的****不是没用了吗,与0000进行与运算后都是0000
所以在计算hashCode的算法中,会对算出来的hashCode进行右移然后异或运算,使得高位的值保留到低位中去
举例 hashcode 为1010 0100
假设右移4位   0000 1010
异或运算 1010 1110
这样高位的这两个1就不会完全丢失了
posted @ 2020-08-02 10:32  苏黎世湖畔  阅读(330)  评论(0编辑  收藏  举报