HashMap的长度为什么是2的N次幂,如何实现的?

问题:

看过HashMap源码的人可能都用印象,就是hashMap的哈希表长度可以由自己指定也可以不指定使用默认长度,但是如果在了解或者发现tableSizeFor方法的话,你就会知道此方法会改变我们的输入长度 (如果我们输入15,他会改为16),那么他为什么要修改我们设置的长度,以及修改后有什么作用?带着这个疑问我们往下看;

HashMap的长度为什么是2的N次幂?

为了能让hashMap存取高效,尽量减少碰撞,也就是要尽量把数据分配均匀。
Hash值的取值范围-2147483648到2147483647,总共有40+亿个映射空间,只要哈希函数映射的比较均匀,一般应用很难出现碰撞,但是内存肯定不能一次加载这么长的数组,所以这个散列值是不能拿来直接用的,我们只能创建合理长度的数组作为哈希表,在插入数据之前做取模运算,得到的余数就是将要存放的数据在哈希表中对应的下标。在HashMap中这个下标的取值算法是:(n - 1) & hash n是哈希表的长度。

取模运算中如果除数是2的幂次方则等价于 其与除数减一的&操作,就是:hash % length == hash & (length - 1)
采用二进制位操作 & 相对于 % 能够提高运算效率,这也就解释了为啥HashMap的长度需要为2的幂次方

HashMap怎么实现的 ?

先看下JDK8的源码:

/**
 * 方法保证了HashMap的哈希表长度总位2的幂次方
 * 返回大于输入参数且最近的2的整数次幂的数
 */
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

分析

n第一次右移一位时,相当于将最高位的1右移一位,再和原来的n取或,就将最高位和次高位都变成1,也就是两个1;
第二次右移两位时,将最高的两个1向右移了两位,取或后得到四个1;
依次类推,右移16位再取或就能得到32个1(如果转换后的二进制有31位的话);
那 要32个1干嘛呢?
二进制数字如果都是1的话,那么他加一后就是首位为1其他位都是0,这个数字肯定是2的幂次方, 2^n == 1<<n

举例说明:

10的二进制是1010,减1就是1001
第一次右移1位取或: 1001 | 0100 = 1101 ;
第二次右移2位取或: 1101 | 0011 = 1111 ;
第三次右移4位取或: 1111 | 0000 = 1111 ;
第四次右移8位取或:1111 | 0000 = 1111 ; (由于10的二进制只有4位,所以,后面再右移得到的都是4个0)
第五次右移16位取或:1111 | 0000 = 1111 ;
最后得到 n = 1111 ,返回值是 n+1 = 2 ^ 4 = 16 ;

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值