啊哈瞬间之tableSizeFor函数

导读:我记得在《编程珠玑》一书中,作者提到了总有一些优秀的编程思想会让人忍不住发出“啊哈”的声音,这有可能是叹为观止,也有可能是恍然大悟。

1. 简介

tableSizeFor是Java的hashmap/concurrenthashmap的源码中比较重要的一个函数,其功能是:返回一个比输入值大或相等的,离该值最近的2的整数次幂。举例:输入值是5,则该函数返回值是8(2的3次幂),输入值是4,则返回4。说实话,在我真正理解这个函数之前,如果你把它的源码放到我面前,让我说出这个函数的功能,我可能真的无法说出答案。在我花时间理解之后,我是忍不住内心发出了“啊哈”的赞叹!

如果你感兴趣,可以先不看后面的介绍,然后对照我上面所说的功能,看看自己是如何实现的?

OK,如果你已经写好了,那让我们看看java原作者是怎么实现的吧?话不多说,先上源码,本文中使用的是JDK11的源码。

 private static final int tableSizeFor(int c) {
        int n = -1 >>> Integer.numberOfLeadingZeros(c - 1);
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

2. 剖析过程

如果你看完上面的几行代码后瞬间就理解了,那么恭喜你,我已经献上了我的膝盖,后面也没必要继续看了。否则的话,我们就试着一起来理解一下吧~~

2.1 -1的二进制表示

  • 取原码:00000000 00000000 00000000 00000001
  • 求反码(逐位求反):11111111 11111111 11111111 11111110
  • 得补码(反码+1):11111111 11111111 11111111 11111111

也就是说-1的二进制表示是:11111111 11111111 11111111 11111111,事实上上面的过程就是求一个负数二进制表示方法的过程。

2.2 位运算

java中常见的位运算包括:按位与&、按位或|、按位非~、按位异或^,也包括>>(右移位),<<(左移位)。

右移位符号>> N通常理解为除以2的N次幂,左移<< N可理解为乘以2的N次幂。那>>>又是几个意思呢?看着也是移位。

  • >>:带符号右移,正数右移后高位补0,负数右移后高位补1,因此4>>1 = 2 ,-4>>1 = -2。
  • >>>:无符号右移,不管正数还是负数,右移后高位都补0。
    因此,对于正数来说,二者返回值一样,但如果是负数,则相差很远,如果你是想实现除以2的效果,那么请用>>吧。

2.3 算法核心

核心在于用“二进制”来思考问题,而不是十进制,对于任意一个整数,换算为二进制之后(以5为例是0101,高位的0省略),那么比它大的2的整数次幂最小的一个值即是高一位置1,其他位置0(以5为例是1000,换算为二进制是8)。那么怎么实现这个想法呢?
作者巧妙的使用了-1进行>>>操作,右移的位数使用了Integer.numberOfLeadingZeros(c - 1),比如c是5,则0100(5-1)前面的0的位数是29,将-1>>>29位得到的是0111;然后再将结果+1,得到1000(8),就是我们期望的结果啦~

先减1再加1这个简直是神操作,既能刚好得到一个2的整数幂的值,而且能避免边界情况,即输入值本身就是2的整数幂的情况,比如输入值为4的时候,如果不减1操作,那么得到的就是8了。

好了,tableSizeFor就解析到这儿了,不知道你啊哈了没?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

普普通通程序猿

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值