计算一个数与2的n次方取模

HashMap的数据是存储在链表数组里面的。在对HashMap进行插入/删除等操作时,都需要根据K-V对的键值定位到他应该保存在数组的哪个下标中。
而这个通过键值求取下标的操作就叫做哈希。
HashMap的数组是有长度的,Java中规定这个长度只能是2的倍数,初始值为16。
求哈希简单的做法是先求取出键值的hashcode,然后在将hashcode得到的int值对数组长度进行取模。为了考虑性能,Java总采用按位与操作实现取模操作。

先看代码:

static int indexFor(int h, int length) {
   return h & (length-1);
}

按理说定位HashMap的角标应该是根据h%length来计算,为啥这里用的是h & (length-1)。原来作者是考虑到
位运算(&)效率要比代替取模运算(%)高很多,主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。

那么,为什么可以使用位运算(&)来实现取模运算(%)呢?这实现的原理如下:

X % 2^n = X & (2^n - 1)

2n表示2的n次方,也就是说,一个数对2n取模 == 一个数和(2^n - 1)做按位与运算 。

假设n为3,则2^3 = 8,表示成2进制就是1000。2^3 -1 = 7 ,即0111。

此时X & (2^3 - 1) 就相当于取X的2进制的最后三位数。

从2进制角度来看,X / 8相当于 X >> 3,即把X右移3位,此时得到了X / 8的商,而被移掉的部分(后三位),则是X % 8,也就是余数。

上面的解释不知道你有没有看懂,没看懂的话其实也没关系,你只需要记住这个技巧就可以了。或者你可以找几个例子试一下。

6 % 8 = 6 ,6 & 7 = 6

10 % 8 = 2 ,10 & 7 = 2

计算一个数与2的n次方取模_第1张图片
image

所以,return h & (length-1);只要保证length的长度是2^n的话,就可以实现取模运算了。而HashMap中的length也确实是2的倍数,初始值是16,之后每次扩充为原来的2倍。

你可能感兴趣的:(计算一个数与2的n次方取模)