计算一个数与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

image

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

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 3,729评论 0 13
  • 定点小数运算 来自:http://www.eepw.com.cn/article/17893.htm 在DSP世界...
    郝宇峰阅读 7,251评论 0 2
  • 差点被尿憋死
    大小刘阅读 154评论 0 0
  • 他的烤串老远就闻得香。诱惑地我最终坐到了小板凳上一解谗虫。每次去他的小摊,都能碰到熟客们大老远地跟他打招呼。有一次...
    那只叫恩典的喵小姐阅读 163评论 0 0
  • 灵魂伴侣 灵魂伴侣是一种灵魂上的精神上的相互连接。 它要求双方是非常高的一个状态,双方要能够不断成长,成长为最高的...
    崔颖魔法师阅读 209评论 0 1