为什么hashmap的容量必须是2的n次幂

Paho Asynchronous MQTT C Client Library--MQTTAsync API 异步MQTT 客户端库for C 阅读详情

 

要明白为什么是2的n次幂,这要从hashmap的hash方式说起,hashmap的容量期望就是用来均匀散列存放map中的元素。hashmap根据hash值把元素放到hashmap内部数组的一个位置上。

1、为什么hashmap的容量必须是2的n次幂??

我们不妨先看看hashCode的原理,以String为列,获取hashCode的方法源码

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

可以看出任何对象的hashCode方法返回一个对应的int类型的结果,所以hashCode的取值范围是-2^31 ~ (2^31-1),即-2147483648~2147483647。

(值得注意的是:两个对象equals相等,hashCode一定相等,hashCode相等,equals不一定相等,从上段代码hashCode源码中可以得出这个结论,会存在不同的数,计算出相同的hashCode)

当要存入map中的条目数为n(0 < n <= 1073741824)的时候,这n个数要散列分布到map数组中,那么map数组的长度为多少,才能使得元素在map数组最为呈现散列均匀分布,又不浪费空间呢?那么就是要求每个数通过某种算法,填入到数组表中的概率是相等的。这种算法最容易想到的就是取模运算。

我们联想下通过运算能不能达到类似效果呢,因为计算机本身就是通过位运算完成所有计算的,通过测试发现位运算比模运算快约27倍。怎么个位运算,使得n个元素能在有指定数组长度的情况下,得到散列分布索引值呢?

参考hashMap获取索引的算法源码

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

indexFor中的h是hashCode通过变换之后的值。怎么变换呢?为什么要变换呢?hashCode结果是一个32位的二进制数,如果直接用如此长的二进制数和目标length-1直接进行与运算结果为怎么样呢?答案是高位(也就是左边的位)会大量丢失。看看下面例子

如果两个数的hashCode分别是

11001110 11001101 11011101 00111110

11001110 11001111 11011101 00111110

只有其中第15位不一样(任何高位不一样都可以,假如我们以16位为划分,任何两个高16位不一样,低16位一样的数),这两个hashCode与length-1做与运算,当length<2的16次方时(1<<16 = 65536)得到两个hashCode & length-1 的结果一样,这样的两个数,却产生了相同的hash结果,于是hashMap想到了一种处理方式:将hashCode的高16位于低16位进行异或运算,其实吧用普通话说就是把高16位的特征放到低16位中,让低低16位同时拥有了高16的特征。这样,在与length-1做与运算时结果就不一样了。其计算过程如下:假如有hashCode a=11001110 11001111 11011101 00111110

int a =  11001110 11001111 11011101 00111110

a >>> 16 = 00000000 00000000 11001110 11001111

a ^ (a >>> 16)

11001110 11001111 11011101 00111110
00000000 00000000 11001110 11001111
=
11001110 11001111 00010011 11110001

用java代码表达 

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

hashCode结果是32位int 结果,将32位的结果做变换,可以从下面例子来研究下

int a =  11001110 11001111 11011101 00111110

a >>> 16 = 00000000 00000000 11001110 11001111

a ^ (a >>> 16)

11001110 11001111 11011101 00111110
00000000 00000000 11001110 11001111
=
11001110 11001111 00010011 11110001

在算法上可以看出,32位hashcode中保持高16位不变,高16与低16异或结果作为新的低16位。然后用hash得到的结果传入方法indexFor获取到hashMap的索引。

hash与n-1 做与运算,这样既在容量远小于1<<16(往往hashmap的容量远小于65535) 的情况下,hash & (n-1),在计算中只有低位((n-1)对应的二进制数相同的位数)参与&运算,计算效率高,同时也保证的hash的高16位参与了索引运算。这样得到的索引能呈较为理想的散列分布,在将条目放入hashMap中时,最大限度避免hash碰撞。

设hashMap容量为16,那么我们看下计算出来的索引值

11001110 11001111 00010011 11110001
&
00000000 00000000 00000000 00001111
=
00000000 00000000 00000000 00000001

索引=1,将该条目存入hash表标号为1的位置处

回到标题中的问题:为什么容量一定要是2的n次幂,只有当length=2的n次幂的时候,length-1的二进制表达 全部为1(15的二进制1111,31的二进制位11111),只有当length-1的全部位都1时,h & (length-1)的结果才能均匀散列在数组中。这时,取模和与运算两种运算才是等价不等效的。至此就解释完了为什么hashMap的容量必须是2的整数次幂。

2、hashmap怎么去计算容量呢?怎么确保程序员传入的值是2的n次幂呢,答案是不能保证。那就只能通过内部算法hashMa自己来实现保证。

先不考虑扩容的情况下,通过传入我们自定义的容量值来构造HashMap实例

    public HashMap(int initialCapacity) {
        // DEFAULT_LOAD_FACTOR = 0.75
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

怎么保证传入的容量一定2的整数次幂,可以从源码来看,通过下面方法等到保证,结果一定是2的整数次幂,结果是大于等于initialCapacity的最小2的整数次幂。

n |= n >>> 1,  符号 | 为或运算,n 与 n>>>1 进行或运算,然后赋值给n

    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 >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

当不指定Hashmap容量是,初始化默认容量为16,当往HashMap中put时,会检查当前hash表中的条目数是否大于容量的0.75倍,如果满足大于容量的0.75,hashMap调用resize方法,新容量 = 原来容量 x 2,在resize时将原来的容量翻倍,然后把之前所有条目复制一遍放入扩容之后的hash表中,这是非常好性能的操作,所以在已知容量数量的情况下避免扩容的情况发生。


 

铁路场景雷达点云目标识别与追踪系统 本文介绍了一种基于毫米波雷达的铁路货运车站安全监控系统。该系统采用分布式架构,通过多个毫米波雷达节点采集点云数据,经过坐标转换、多雷达融合等处理后,利用机器学习算法实现列车、行人等目标的实时识别与追踪。系统包含数据采集层、处理层、目标识别层、追踪层、预警决策层和可视化层,能够克服传统视频监控受环境影响大的缺点,实现全天候可靠监测。文中详细展示了雷达数据解析和点云预处理的代码实现,包括坐标转换、数据格式解析和体素网格滤波等关键技术。该系统可有效预警铁路场景下的碰撞风险,提升货运车站的安全管理水平。 阅读详情

相关推荐

电磁阀的符号解读

先数方框数量定“几位”(工作位置数 );数接口数量(P、A、R等 )定“几通”;看箭头、截断符号,结合常态位(弹簧侧/中间位 ),分析通电/断电时的通路切换;字母(P、A、R )、线圈(单/双电控 )、弹簧(复位 )辅助确认功能(如NO/NC对应常态通断 )。不同厂家符号细节(如编码、线圈画法 )有差异,遇到陌生符号,建议结合产品手册、品牌规范核对,就能精准读懂电磁阀的“语言”啦~

创新改变世界! 6893

HashMap容量为什么是2的n次方?如何做到的?

HashMap无疑是我们最常用的集合之一了,当我们调用HashMap(9)构建map的时候,它的容量是多少呢? 划重点!HashMap(9)构建的map的容量是16!!!这也就引出了下面的问题: HashMap的容量为什么是2的n次方?HashMap是如何保证容量是2的n次方的? HashMap容量取2的n次方,主要与hash寻址有关。在put(key,value)时,putVal()方法中通过i = (n - 1) & hash来计算key的散列地址。其实,i = (n - 1) &

进击的Coder*的博客 7423

RJ45以太网接口EMC设计方案

RJ45以太网接口EMC设计方案 一、 接口概述 RJ45以太网接口是目前应用最广泛的通讯设备接口,以太网口的电磁兼容性能关系到通讯设备的稳定运行。赛盛技术应用电磁兼容设计平台(EDP)软件从接口原理图、结构设计,线缆设计三个方面来设计以太网口的EMC设计方案。 二、 接口电路 原理图的EMC设计 百兆以太网接口2KV防雷滤波设计 图1 百兆以太网接口2KV防雷滤波设计 接口电路设计概述: 本方案从EMC原理上,进行了相关的抑制干扰和抗敏感度的设计;从设计层次解决EMC问题;同时此电路兼容了百兆以太网接

兰陵笑笑生SD的博客 1万+

hashmap扩容为什么是2的幂

HashMap的初始容量都是2的n次幂的形式存在的,而扩容也是2倍的原来的容量进行扩容,也就是扩容后的容量也是2的n次幂的形式存在的,下面就来说明一下为什么是2的n次幂的形式! 先来看一下源码,也就是向HashMap中添加元素,或者扩容时是怎么存放元素的。 第一个截图是向HashMap中添加元素putVal()方法的部分源码,可以看出,向集合中添加元素时,会使用(n - 1) & hash ; n = table.length的计算方法来得出该元素在集合中的位置;而第二个截图是HashMap扩容

一个搬砖工人 1922

SAP 物料账前期没有运行的解决方法

If you use the actual costing (material ledger), you must have completely closed all period-end closing operations in the actual costing for the penultimate period prior to the period shift. Use the '...

含笑饮砒霜死后更嚣张 3316

爆肝5万字❤️Open3D 点云数据处理基础(Python版)

本文为Open3D 点云数据处理基础教程(Python版),小白也能轻松上手!还等什么呢,学起来!

孙悟空 13万+

一文看懂HashMap扩容为什么总是2的次幂

点击上方“后端技术精选”,选择“置顶公众号”技术文章第一时间送达!作者:helloword先生https://blog.youkuaiyun.com/u010841296/HashMap是根据key...

Java笔记虾 3217

国产系统(统信 UOS )漏洞修复方式

2.1 服务器版(单漏洞更新):服务器标准版:yum upgrade-minimal --cve “CVE编号”服务器 D 版:sudo apt update&&sudo apt install “受影响的软件包”1 统信 UOS 漏洞修复方式在线升级修复桌面专业版:通过以下步骤打开在线升级:控制中心–>更新–>更新设置–>打开系统更新、安全更新。安装过程中,请勾选“我已阅读并知晓”,然后点击“开始升级”。2 点击“检查更新”–“全部下载”,如果不想进行系统更新,可以选择只下载“安全更新”。

Reyn_观极 5013

HashMap每次扩容时,为什么都必须是2的N次方?

HashMap的底层是通过的数据结构来存放数据的。我们知道,那么问题来了,为什么HashMap会将首次初始化容量设置为16,而后续每次扩容都是之前的2倍?而不是像ArrayList首次为10,后续为1.5倍呢?这可是我们在面试时的一个高频考点哦!

一一哥 2618

软件可靠性测试与评价

本文系统介绍了软件可靠性工程的核心内容,包括可靠性概念、模型分类、测试方法和评价体系。重点解析了可靠性测试的三大步骤:定义运行剖面、设计测试用例和实施测试收集数据,并以智能安防系统为例说明具体应用。可靠性评价需结合业务场景,通过量化指标(MTBF、失效率等)和模型预测持续优化系统。文章强调可靠性需贯穿软件全生命周期,通过闭环反馈实现持续改进,为关键系统提供质量保障。

moton2017的博客 2050

EndNote X9 快速上手教程(毕业论文参考文献管理器)

EndNote X9 快速上手教程一、EndNote X9安装1.安装2 安装之后重要步骤二、第一次打开 EndNote三、参考文献引用1. 导入知网文献2. 参考文献引用四、常见问题1. 引用文献中出现%J的问题2 参考文献中出现et.al1 在文章中引用的编号的地方出现2 在参看文献列表里出现 一、EndNote X9安装 1.安装 打开文件夹 安装前,一定要读 #Readme.txt 文件 一定要把License.dat和ENX9Inst.msi放在同一文件夹,安装的时候会自动激活。 我的安装路

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 2万+

HashMap 的长度为什么是 2 的 N 次方呢?

2. 扩容时的重新哈希: 当 HashMap 的元素个数达到容量的 3/4 时(Java 8 及之前为 0.75,Java 8 及之后为 0.625),HashMap 会进行扩容操作。这样在重新计算哈希值时,只需将原来的哈希值的高位与新的容量进行与操作(取模),就可以得到新的位置。1. 散列冲突的解决: 在 HashMap 中,元素存储位置是根据元素的键的哈希值决定的。采用长度为 2 的幂次方可以确保通过哈希值的某些位掩码(取模操作)来定位到数组中的位置,这样可以减少哈希碰撞的概率,提高查找效率。

NZC2237的博客 1623

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

HashMap:为什么容量总是为2的n次幂

HashMap:为什么容量总是为2的n次幂 1)、HashMap是根据key的hash值决定key放到哪个桶中,通过tab[i = (n - 1) &amp; hash]公式计算得出 这里的n是HashMap的容量,因为n永远是2的次幂,所以n - 1通过二进制表示,永远都是末尾连续1的形式表示(如00001111、00000011),当(n - 1) 和hash做与运算时,会保留hash中后x位...

hxt的博客 5194

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

HashMap初始容量为什么是2的n次幂及扩容为什么是2倍的形式

接着上一篇博客,上一篇博客说明了HashMap的初始容量都是2的n次幂的形式存在的,而扩容也是2倍的原来的容量进行扩容,也就是扩容后的容量也是2的n次幂的形式存在的,下面就来说明一下为什么是2的n次幂的形式! 先来看一下源码,也就是向HashMap中添加元素,或者扩容时是怎么存放元素的。 第一个截图是向HashMap中添加元素putVal()方法的部分源码,可以看出,向集合...

Apeopl的博客 4万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

HashMap——HashMap的容量为什么是2的n次幂

由此我们可以看出当哈希数组长度为奇数时是极易发生哈希碰撞,导致我们的数组空间不能被充分利用且。过长,降低查找的效率而哈希数组长度为偶数时发生哈希碰撞的概率是比较小的,所以数组长度在考虑。来计算索引值,key&(length-1)这两种计算方式并无太大区别,只是相对于。为key%(length-1)但这样在计算机中计算效率比较低,所以我们采用。被设置为16,而在我们通过key去计算哈希值(索引值时)计算一般采用。我们在计算哈希值时发生哈希碰撞是不可避免的,但是怎样才能。位运算:都为1时则为1否则为0。

Lemon_man_的博客 399

HashMap、ConcurrentHashMap的容量为什么总是2的n次幂

HashMap、ConcurrentHashMap的容量为什么总是2的n次幂

just_for_that_moment的博客 545

HashMap初始容量为什么是2的n次幂及扩容为什么是2倍的形式?

前言 逛了一圈发现大家对于这个问题的回答写的都比较散乱,简而言之两点原因: 1.为了保证得到的新的数组索引和老数组索引一致 2.rehash时的取余操作,hash % length == hash & (length - 1)这个关系只有在length等于二的幂次方时成立,位运算能比%高效得多 论述 1.为了保证得到的新的数组索引和老数组索引一致。HashMap的初始容量是2的n次幂,扩容也是2倍的形式进行扩容,是因为容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少has

Apollo的博客 1万+

HashMap的数组容量为什么是2的N次幂

HashMap的数组容量为什么是2的N次幂,首先我们要清楚数组的索引值是0到N-1,而hashCode的计算范围是42亿,也就是所有对象的hashCode有42亿种可能,我们要把它丢到这16的长度的数组里面时,我们应该怎么做?怎么将42亿种可能变为0到N-1种可能,通常我们会想到用取模的方法来计算,但是它有两个缺点: 1、负数求余还是负数 2、 性能较慢(对比位运算) 我们来看看HashMap源码是怎么做的,它用的是不是取模运算呢?向HashMap中添加元素,或者扩容时是怎么存放元素的。 第一张图,向

weixin_44421311的博客 1221

为什么jdk1.8 HashMap的容量一定要是2的n次幂

一、jdk1.8中,对“HashMap的容量一定要是2的n次幂”做了严格控制   1.默认初始容量: [Java]纯文本查看复制代码 ? 1 2 3 4 /** * The default initial capacity - MUST be a power of two.(默认初始容量——必须是2的n次幂...

黑马程序员广州中心的专栏 860

为什么HashMap的容量必须为大于该容量的最小的2的n次幂

调用HashMap的put方法将一个键值对存到map中,会先计算key的散列值。该散列值是int基本类型,int的范围为~,用这个散列值来确定map的数组下标(jdk8之前HashMap的数据结构是数组+链表,jdk8之后又加上了红黑树)。但这散列值太大,map的容量不可能这么大,会内存溢出。所以需要通过一种方式将这个key的散列值与map的容量对应起来。比如说将散列值与map容量取模,这样得到的结果一定小于容量了,就可以拿来做数组下标了,但取模计算的速度较慢。jdk采用了这种方式计算下标: (n -

jingzuangod的博客 989

HashMap的容量为什么是2的n次幂小记

HashMap的容量为什么是2的n次幂,和这个(n - 1) & hash的计算方法有着千丝万缕的关系,符号&是按位与的计算,这是位运算,计算机能直接运算,特别高效,&的计算方法是,只有当对应位置的数据都为1时,运算结果也为1,当HashMap的容量是2的n次幂时,(n-1)的2进制也就是1111111***111这样形式的,这样与添加元素的hash值进行位运算时,能够充分的散列,使得添加的元素均匀分布在HashMap的每个位置上,减少hash碰撞,下面举例进行说明 由图...

cj_eryue的博客 502

HashMap的容量为什么是2的n次幂

总结:为了减少哈希冲突 为什么能减少哈希冲突,因为容量的大小跟确定元素在桶中的位置有关, 元素在桶中的位置由(n-1)&hash计算得来 当容量大小不够大时,元素的位置主要用的hash低位的信息,那么结合&操作,&的另一个数最好低位全是1,而2的整数次幂-1的低位全是1,这样&才有意义;否则结果就肯定是0那么&就没有意义,冲突概率增加 ...

alphadunk的博客 253
上一篇: mysql 数据库索引b+树深度问题
下一篇: hashMap1.7头插法及扩容
小涛_foxiaotao
博客等级 码龄14年 2粉丝 52原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值