hashmap为什么长度要是2的n次幂
1、使用位运算(&)来实现取模运算(%)
在jdk1.7源码中,hashmap中有一方法,叫做indexFor(),该方法其实主要是将hash生成的整型转换成链表数组中的下标。我们理所应当的应该认为会使用key的hash值对着数组总长度取模得到所引下标,但它的代码是这样的:
return h & (length-1)
那这段代码是什么意思呢?其实,他就是取模。Java之所有使用位运算(&)来代替取模运算(%),最主要的考虑就是效率。位运算(&)效率要比代替取模运算(%)高很多,主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。
X % 2^n = X & (2^n – 1)
2n表示2的n次方,也就是说,一个数对2n取模 == 一个数和(2^n – 1)做按位与运算 。
假设n为3,则2^3 = 8,表示成2进制就是1000。2^3 = 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
所以,return h & (length-1);只要保证length的长度是2^n的话,就可以实现取模运算了。而HashMap中的length也确实是2的倍数,初始值是16,之后每次扩充为原来的2倍。
2、扩容时,元素的位置迁移
我们知道,hashmap在元素个数达到 元素个数阈值*加载因子 的时候,会进行扩容操作,扩容一定意味着之前的元素需要发生位置的迁移。
而面对元素的迁移,结合1,我们不难想到用来替代取模运算的&运算重新对每一个元素进行重新散列计算所在索引位置。
但实际上,这种操作也并非是最优解,还有什么办法呢? 那就是只需看该元素的hash值二进制位上与此次扩容后长度-1(2^n-1)的二进制位上的最高位对应的那位是0还是1,如果是1,则不需要变化,因为1 & 1还是1,如果是0 ,则只需要让索引值+新多出来的那1位的值。
举个栗子:
有一个元素X,hash后的值为29,二进制为: 0001 1101
所要放入的map的内部数组长度为16,加载因子为0.75
此时根据上述位运算求索引的方法,使用hash(X) & 数组.length-1,
29==> 0001 1101
&
16-1=15==> 0000 1111
0000 1101
由此可以得出,求出的索引值为13
当随着元素的不断增加,达到了数组扩容的阈值: 16x0.75=12
此时,对数组进行2倍扩容,变成长度为32的数组。
然后将对原有元素进行移动位置,以上述X为例,
先使用我们2中总结出来的快捷算法来进行计算:
原索引值为13
32比16在二进制上多出的那一位,也就是16
此时,我们算出X元素在扩容后的位置应当是 13+16=29
接下来,我们使用重新&运算来进行计算,
12==> 0001 1101
&
32-1=31==> 0001 1111
0001 1101
由此可以得出,求出的索引值为29
并且从&运算的运算过程中也可以看到,X原有位上(后4位)数值不管是和16比还是和32比,都是没有变化的,因为不管是16还是32,这几位上面都是1。
而唯一可能会发生变化的,则是取决于X对应的新一位上的数是0还是1,是0的话,数压根就不会变。 是1的话, 只需要原有值+加上这多出来的1位的值就可以得到新的散列值了
但不得不提,这一切,都要得益于只有长度在2的n次幂的情况下才能够使用。
那hashmap里是怎么应用的呢?
关于2的源码部分:
在jdk1.8中的hashmap源码中,就运用了这个技巧来对resize后的元素索引重排进行优化。
核心代码是:e.hash & oldCap== 0
通过该逻辑将旧数组里的每一个链表元素分为两部分,上述代码==0和!=0的,之后 ==0的在新数组的位置不变,!=0的 用原有索引值+老的数组长度 得到的索引就是它的新位置
那为什么可以用这个逻辑来决定元素到底需不需要改变索引值呢?通过之前的描述,我们其实只需要知道扩容后新增的那位上,该元素的值到底是0还是1, 那如何得到这个值?
e.hash & oldCap就可以得到!
也就是 元素hash &原有数组的长度
此时,数组长度二进制位上,低位都为0,高位为1
此时,元素的hash的与高位的对应位与高位进行&运算,自然知道是0还是1!
沿用之前的例子,有一个元素X,hash后的值为29(e.hash),二进制为: 0001 1101
所要放入的map的内部数组长度为16(oldCap)
此时使用上面的算法,过程:
29==>0001 1101
16==>0001 0000
0001 0000
可知结果是1。
参阅:深入理解hashmap
hashmap为什么长度要是2的n次幂相关推荐
- 为什么HashMap的长度一定是2的次幂?
HashMap是面试过程中最常问的知识点之一 今天用最通俗易懂的大白话来讲一讲:为什么HashMap的长度一定是2的次幂? 大家知道HashMap中,如果想存入数据,首先它需要根据key的哈希值去定位 ...
- HashMap 的长度为什么是 2 的幂次方?
HashMap 的长度为什么是 2 的幂次方? 为了能让HashMap存取高效,尽量减少碰撞,需要将散列表的数据分配均匀.使用HashMap查询或插入数据时,需要先对数组长度取模运算,index = ...
- HashMap 的长度为什么是 2 的幂次方
HashMap 的长度为什么是 2 的幂次方 为了让HashMap存取高效,数据分配均匀从而减少碰撞. 因为将数据存储到链表中的算法是hash&(length-1),length是2的n次方, ...
- Hashmap链表长度为8时转换成红黑树,你知道为什么是8吗
为什么要转换? 每次遍历一个链表,平均查找的时间复杂度是 O(n),n 是链表的长度.红黑树有和链表不一样的查找性能,由于红黑树有自平衡的特点,可以防止不平衡情况的发生,所以可以始终将查找的时间复杂度 ...
- 被替换的项目不是替换值长度的倍数_面试官,为啥HashMap的长度是2的n次方?
前言 HashMap的主干是一个数组,假设我们有3个键值对dnf:1,cf:2,lol:3,每次放的时候会根据hash函数来确定这个键值对应该放在数组的哪个位置,即index = hash(key) ...
- Java 集合List、Set、HashMap操作一(Array转List、Set排序、HashMap遍历、Set遍历、List遍历、HashMap大小长度、List打乱顺序)
数组转集合(Array转List) import java.util.*; import java.io.*;public class ArrayToCollection{public static ...
- HashMap初始化长度设置大小
HashMap的优化点,创建HashMap时,如果已经知道大概要放多少的数据量,可以自己设置好长度,减少扩容,提高速度. 代码比较,存放3个数. 不设置初始化大小 private static voi ...
- 为什么HashMap链表长度超过8会转成树结构
HashMap在JDK1.8及以后的版本中引入了红黑树结构,若桶中链表元素个数大于等于8时,链表转换成树结构:若桶中链表元素个数小于等于6时,树结构还原成链表.因为红黑树的平均查找长度是log(n), ...
- HashMap的长度为什么是2的N次方
1.减小哈希冲突概率 假如当前Entry数组长度为len,插入节点时,需要对key的hashcode进行二次哈希,然后跟len-1相与(得到的值一定小于len,避免数组越界) 如果len是2的N次方, ...
最新文章
- 【Netty】mmap 和 sendFile 零拷贝原理
- mahout基于Hadoop的CF代码分析(转)
- hackme_Login As Admin 0
- 第三次学JAVA再学不好就吃翔(part11)--基础语法之switch语句
- spine 导出纹理_Spine 纹理打包Texture packing_官方文档中文版
- 设计模式C++实现(7)——装饰模式
- c# 替换html注释,C# 替换div标签
- mysql怎么避免联合查询_mysql-联合查询,连接查询
- IIS中启用ASP并连接Access数据库的解决办法
- 微软超融合私有云测试02-测试架构描述
- JavaScript学习笔记 及 JAVAScript优化
- 10GE DWDM SFP+彩色光模块应用案例
- android 模拟menu键点击事件,android处理Back键Home键和Menu键事件(转)
- win10小课堂:如何彻底关闭windows defender
- Java中涉及到金钱计算方法
- matlab优化工具箱OptimizationToolbox使用方法
- 小红书笔记下沉怎么做到的?
- Windows下使用bat脚本批量创建文件夹
- linux怎么设置wifi密码,技术|怎样在 Arch Linux 终端上更改 WiFi 密码
- 分享个免费的货币汇率API
热门文章
- 亚马逊站点之土耳其站
- 阿里云申请免费CA认证
- 基于PaddleOCR和ToolGood.Words测试从图片中检测敏感词
- LabVIEW编的上位机控制汇川PLCH5U和汇川伺服运动 ,海康威视相机视觉对位,LabVIEW通过网口控制汇川H5U和Ethercat伺服
- 芝村乡如何从零开始学习理财
- matlab 风电叶片气动计算程序,基于Matlab与Solidworks方法的风力机叶片优化设计
- Linux(Ubuntu)使用setsid命令后台运行python代码并记录终端输出,并实现开机自启
- R语言使用dbern函数生成伯努利分布(0-1分布)密度数据、使用plot函数可视化伯努利分布密度数据( Bernoulli distribution)
- 三菱PLC的嵌入式软PLC技术的应用
- 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式国赛客观题以及详细题解