记HashMap中的tableSizeFor(寻找最小二次幂算法)

    /**
     * Returns a power of two size for the given target capacity.
     */
    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;
    }

趁着周五下班前的一点时间我又复习了一遍HashMap的源码

注意到了这个位运算方法,这个算法的目的是求出大于等于给定数字的最小二次幂。之前好像群里有人把这道题提到leetcode上去了,今天看到了就了解了一下。

第一眼看上去,很神奇,怎么就几个位运算就求出了最小二次幂呢。。。。

然后思考。。。不得其解,与群友讨论,有人给出了简略答案,答案我都看了好几遍才看懂。。。

首先我们只看五次位运算:

        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;

ps:这里的>>>指的是无符号右移,就是最高位符号位一起移动(因为HashMap的大小已经先判断过了  肯定是正数)

首先我们假设一个数的二进制表示是:

Step1

n0..01...(肯定有一个最高非0位,n==0的情况我后面会提到)
n>>>10..001...
n=n|n>>>10..011...

这里的作用实际上就是把最高非0位向右扩散成了两位

Step2

n0..011...(从“最高非零位”开始2位是1)
n>>>20..00011...
n=n|n>>>20..01111...

把最高非0位向右扩散成了四位

Step3

n0..01111...(从“最高非零位”开始4位是1)
n>>>40..000001111...
n=n|n>>>40..011111111...

把最高位的1向右扩散成了八位

 

....

Step5

n0..01111111111111111...
n>>>160..000000000000000001111111111111111...
n=n|n>>>160..011111111111111111111111111111111...(32个1)

 

忽略无符号int是32位的因素  最后得到的二进制就是这样

最高非0位前面的0始终没有收到影响,最高非0位向后扩散成了32个1,而右移操作如果移超出32位的都会被舍弃。

所以最后得到的就是:最开始的n从最高非0位后面的位数全变成1。

如(21)10101变成了11111变成了31

最后再加1就让所有的1变成0,最高非0位往前移动1位,得到了最小二次幂

(31+1=32是21的最小二次幂)

到这这个方法就差不多分析完了,还剩下几个小问题没解决:

1.最开始的n-1

最开始的n-1应该是为了出现本身n就是最小二次幂,如果不减1就会出现(4的最小二次幂是8)。

特殊的:n=1时,n-1=0没有1传播,传播的就是0,最后0+1=1也是最小二次幂

2.什么情况下位移之后的n会小于0呢

在HashMap中,n肯定是大于0的正整数,理论上来说不可能小于0呀,甚至说就算n小于0,无符号右移是会把最高位的负数位(1)右移成数据位的吧,最高位肯定是0呀,难道写小于0是习惯性的排除无法想象的情况?

希望知道问题二的朋友能够留言告诉我,谢谢

 

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论 3

打赏作者

tian2342

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值