位运算那些骚操作,HashMap容量为什么是2的次幂?

位运算那些骚操作,数不甚数,就拿HashMap来说,在求出元素位置时候通过h & (length-1) 来计算,h是key的hash值,length是数组长度。但是这个length大小是有限制的,必须是2的次幂。那为什么是2的次幂呢?其实这个还算好理解。

首先要知道&的规则,也就是双方都为1,结果才为一,如下:

接着要知道2的次幂的二进制规律,发现开头都为1,剩余全为0。

 public static void main(String[] args) {
     for (int i = 0; i < 10; i++) {
         toBin((int)Math.pow(2,i+1));
     }
 }
 private  static  void toBin(int n){
     System.out.println(Integer.toBinaryString(n)) ;
 }

那么按照HashMap计算规则hash & (length-1),length(是2的次幂)-1后的规律就是这样的,哦,我的天哪,真是神奇的规律。结果全为1。

 public static void main(String[] args) {
     for (int i = 0; i < 10; i++) {
         toBin((int)Math.pow(2,i+1)-1);
     }
 }
 private  static  void toBin(int n){
     System.out.println(Integer.toBinaryString(n) +"  十进制=="+ n) ;
 }

那现在就可以举个计算的例子了,假如h=456,length=16,那么,最终在二进制层计算是这样的,为8,也就是这个元素放入索引为8的位置。

这时候就能体现精髓了,假如hash & (不是2的次幂-1)的数,比如&上了14,那最终结果是这样的,结果虽然也是8。看似也没有问题。实则不然,14的最后一位是0,那就是说hash不论怎么横七竖八的取值,最后算出来的值就不可能是1,就导致数组中索引为1的位置就永远放不到值。在举个例子,11的二进制是1011,那么,从这个0开始,左边全部舍去,右边只要第一位带有1的数都不会被取到,如100(4),101(5),110(6),111(7)。也就是这4个空间要浪费。


或许用下面的代码段能很好的说明。发现取值能很好的分散开。

public static void main(String[] args) {
    int size =16;
    for (int i = 0; i < 10; i++) {
        int r =new Random().nextInt(Integer.MAX_VALUE);
        System.out.println((size-1)&r);
    }
}


但是如hash & (不是2的次幂-1)进行计算,结果却是这样的。取值惨不忍睹

  public static void main(String[] args) {
      int size =16;
      for (int i = 0; i < 10; i++) {
          int r =new Random().nextInt(Integer.MAX_VALUE);
          System.out.println((size)&r);
      }
  }

简而言之,必须是2的次幂,就是为了保证在真正运算时候,二进制位都为1,减少碰撞,位置分配的均匀一些。当然%也能计算索引位置,但是位运算效率高,比%快,还有人问为什么不是2的倍数,其实通过上面的计算,2的倍数的二进制数是可能有某一位是0的,这样计算的话有些索引位置还是可能放不到数据的,如果全为1的话则不然。

必须是2的次幂又牵扯出另一个问题,为什么扩容是原来的2倍?那更好理解了,也许这个动画更能说明,不断往后填0,也就是比原来的数大了一倍,这就又保证length-1的二进制位全为1。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值