jdk8 Map tableSizeFor理解

在 jdk8 的 HashMap 中做了很大的改动,使得Map的性能大幅提升,其中有这么一段代码:

/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
	int n = cap - 1;
	n |= n >>> 1;
	n |= n >>> 2;
	n |= n >>> 4;
	n |= n >>> 8;
	n |= n >>> 16;
	return (n < 0) ? 1 : n + 1;
}

这个方法是用于找到大于或等于输入 cap 的最小的2的幂,比如当我输入5时,大于或等于5的最小的2的整数次幂为8(23),则该方法返回8。

输入(整数形式)输入(二进制形式)返回(整数形式)返回(二进制形式)返回值(2的幂形式)
100011000120
200102001021
300114010022
401004010022
501018100023
601108100023
701118100023
810008100023

通过这个方法可以实现提前设置一个较为稳定的容量,从而避免频繁扩容导致的性能下降。

这个方法的实现原理比较有趣,位运算占据了方法的主体。为了分析整个运算,我这里简化了一下,只保留了有效操作:
在这里插入图片描述

>>>无符号位右移

>>> 是无符号右移运算,无符号表示运算不考虑符号位。对于int类型的数而言,符号位为最高位。当我想要表示一个正数2时,最高位符号位为0,对应的结果为:
在这里插入图片描述
当我想要表示一个负数-2,则最高位符号位为1,其余位要用补码表示:
在这里插入图片描述
无符号右移表示最高位符号位也参与位移运算,如果对 -2 使用无符号右移的话,由于符号位的"1"被移动到了其他位上,此时我们会得到一个非常大的正整数。
在这里插入图片描述
但这明显与我们预期不符,因此无符号位右移通常运用在正整数上。算术上的效果展现为除以2数次幂。
在这里插入图片描述

n |= n >>> 1 分析

我们回到代码上来,n |= n >>> 1不仅仅表示无符号右移,而且同时按位或了n本身,最终又将计算得到的结果赋值给了n。

直接看的效果应该不明所以,不知道这样是什么意思。但是如果我们一位一位来看的话应该会清楚一些,由于有按位右移逻辑,我们保留最低位为0。这样的话我们可以带入n=2,执行一下看看会发生什么效果:
在这里插入图片描述
图片上表现出来的效果可能不太清楚,但宏观上表述的话就是最高位的1填补了低它一位的位置。也就是20010 中的1填充了低它一位的位置,最终变成了30011

再进一步分析的话会发现最高位低一位的位置是0或1对这个运算结果都没什么影响,因为最终都会被高位的1填充:
在这里插入图片描述

位移或运算

理解了这里的代码后,我们不难发现我们只需要研究最左边为1的哪一位对它身后的小兄弟做了什么即可:
在这里插入图片描述
其中最后一步n|n>>>16
在这里插入图片描述
简单来说既是不论我们输入一个什么样的正数,在 return 前我们最终都会得到一个2n-1的数。接下来再加1就能后得到我们预期的结果了。

为什么要 cap-1 ?

回到开头我们来研究一下 int n = cap - 1起到了什么样的作用。此时如果我们输入一个15,程序执行到该行会得到一个14:
在这里插入图片描述
好像没什么作用。因为不论你怎么减,只要不影响到最高位的1,最终结果都不变的。

但实际这里处理的是输入参数为 2 的整数次幂的值。如当输入值为16时:
在这里插入图片描述
这里当我们去掉减一操作后再试试:
在这里插入图片描述
二进制的展现效果如下:
在这里插入图片描述
也就是说最开始的减一操作当我们输入了2的整数次幂时,返回其本身,而不是将扩充一倍。

参考资料

HashMap源码注解
HashMap中tableSizeFor解读

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值