HashMap中数组下标的计算,为什么HashMap的容量是2的倍数?

本文解释了哈希值如何通过与数组长度减一进行与运算来计算HashMap的数组下标,以及为何选择2的整数次幂长度的原因,包括提高计算效率、减少冲突和便于扩容时的元素分布。
摘要由CSDN通过智能技术生成

数组下标计算概述

理论上,哈希值(哈希码)是一个 int 类型,范围从-2147483648 到 2147483648。前后加起来大概 40 亿的映射空间,只要哈希值映射得比较均匀松散,一般是不会出现哈希碰撞(哈希冲突会降低 HashMap 的效率)。

但问题是一个 40 亿长度的数组,内存是放不下的。HashMap 扩容之前的数组初始大小只有 16,所以这个哈希值是不能直接拿来用的,用之前要和数组的长度做与运算用得到的值来访问数组下标才行。

i = (n - 1) & hash

为什么是计算(n - 1) & hash?(n是数组长度)

对于 HashMap 来说,它需要通过hash%table.length来确定元素在数组中的位置,这种做法可以在很大程度上让元素均匀的分布在数组中。

比如说,数组长度是 3,hash 是 7,那么 7 % 3 的结果就是 1,也就是此时可以把元素放在下标为 1 的位置。

当数组的长度是 2 的 n 次方时,hash&(length-1)=hash%table.length。 & 运算比 % 更加高效,因此用(n - 1) & hash计算数组下标。

比如说 9 % 4 = 1,9 的二进制是 1001,4 - 1 = 3,3 的二进制是 0011,9 & 3 = 1001 & 0011 = 0001 = 1。

hash%table.length与hash&(length-1)为什么相等?

从二进制角度来看,hash / length = hash /2 ^{n} = hash >> n,即把 hash 右移 n 位,此时得到了 hash / 2 ^{n} 的商。而被移掉的部分就是hash %2 ^{n} ,就是余数。

例如9/4为9/2^{2}。9的二进制是1001,向右移动两位为0010。9/4的商为0010,就是2。余数是被移掉的部分,01。所以余数为1。

可见:二进制表示中,高于 n 位的所有数值对余数结果没有贡献,只有低于这个阈值的部分才决定余数。

比如说 26 的二进制是 11010,要计算 26 % 8,8 是2^{3},所以我们关注的是 26 的二进制表示中最低 3 位:11010 的最低 3 位是 010,26 % 8 的结果是 2。

当执行hash&(length-1)时,实际上是保留 hash 二进制表示的最低 n 位,其他高位都被清零。

例如26的二进制是11010,length为8,length-1为7。7的二进制是0111。进行&运算结果为2。

数组长度-1正好相当于一个“低位掩码”——这个掩码的低位最好全是 1,这样 & 操作才有意义,否则结果就肯定是 0。

这也正好解释了为什么 HashMap 的数组长度要取 2 的整次方

问题:为什么HashMap的容量是2的倍数?

1、加快计算:当数组的长度是 2 的 n 次方时,hash&(length-1)=hash%table.length。 & 运算比 % 更加高效。

2、减少冲突:

length 为偶数时,length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性

例如 length = 4,length - 1 = 3, 3 的 二进制是 11
若此时的 hash 是 2,也就是 10,那么 10 & 11 = 10(偶数位置)
hash = 3,即 11 & 11 = 11 (奇数位置)

而如果 length 为奇数的话,很明显 length-1 为偶数,它的最后一位是 0,这样 hash & (length-1) 的最后一位肯定为 0,即只能为偶数,这样任何 hash 值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间

length = 3, 3 - 1 = 2,他的二进制是 10
10 无论与什么树进行 & 运算,结果都是偶数

因此,length 取 2 的整数次幂,是为了使不同 hash 值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列

3、在扩容时,利用扩容之后的大小也是2的倍数,将已经产生的hash碰撞的元素完美的转移到新的table中去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值