HashMap扩容机制解读[通俗易懂]

HashMap扩容机制解读[通俗易懂]扩容机制什么时候需要扩容:当hashmap中的元素个数超过数组大小*loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR)是0.75这是一个折中的取值,也就是说,默认情况下数组大小为16,那么当hashmap中的元素个数超过16*0.75=12(阈值或者边界值的时候)就把数组的大小扩展Wie2*16=32,然后重新计算出每个元素在数组中的位置,而这是一个非常耗性能的操作,所以我们最好能够提前预知并设置元素的个数。注

大家好,又见面了,我是你们的朋友全栈君。

扩容机制

什么时候需要扩容:

当hashmap中的元素个数超过数组大小 * loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR)是0.75这是一个折中的取值,也就是说,默认情况下数组大小为16,那么当hashmap中的元素个数超过16*0.75 = 12 (阈值或者边界值的时候)就把数组的大小扩展为2 * 16 = 32,然后重新计算出每个元素在数组中的位置,而这是一个非常耗性能的操作,所以我们最好能够提前预知并设置元素的个数。

注意:
当hashmap中的其中一个链表的对象个数达到了8个,此时如果数组长度没有达到64,那么hashmap会先扩容解决,如果达到了64,就会变成红黑树,节点类型由Node变成TreeNode类型,当然如果映射关系被移除后,下次执行resize()方法时会判断树的节点个数低于6也会再把树转换为链表

什么是扩容:

  • 进行扩容,会伴随着一次新的hash分配,并且会遍历hash表中所有的元素,是非常耗时的,在编写程序的过程中,要尽量避免resize()

  • 每次扩容都是翻倍的与原来的 (n-1)& hash 结果相比,只是多了一个bit位,所以节点要么就在原来的位置,要么就会被分配到 “原位置+旧容量”这个位置

在这里插入图片描述

原数组长度 : 16 n = n - 1 ---> 15
(n - 1) & hash
		 	 0000 0000 0000 0000 0000 0000 0001 0000   16
			 0000 0000 0000 0000 0000 0000 0000 1111   15  n - 1
hash1 (key1):1111 1111 1111 1111 0000 1111 0000 0101    
----------------------------------------------------------------
			 0000 0000 0000 0000 0000 0000 0000 1111   索引 5
			 
			 0000 0000 0000 0000 0000 0000 0000 1111   15  n - 1
hash2 (key2):1111 1111 1111 1111 0000 1111 0000 0101   
----------------------------------------------------------------
			 0000 0000 0000 0000 0000 0000 0000 1111   索引 5
================================================================
数组长度扩容 ——> 16 * 32 n - 1 ---> 31
(n - 1) & hash
			 0000 0000 0000 0000 0000 0000 0010 0000   32
			 0000 0000 0000 0000 0000 0000 0001 1111   31 n - 1
hash1(key1): 1111 1111 1111 1111 0000 1111 0000 0101   
----------------------------------------------------------------		 
			 0000 0000 0000 0000 0000 0000 0000 0101  索引 5
			 
			 0000 0000 0000 0000 0000 0000 0001 1111   31 n - 1
hash2 (key2):1111 1111 1111 1111 0000 1111 0000 0101   			
----------------------------------------------------------------			
			 0000 0000 0000 0000 0000 0000 0001 0101  索引 5 + 16

因此元素在重新计算hash之后,因为N变为2倍,那么n-1的标记范围在高位多1bit 因此新的index就会发生这样的变化
在这里插入图片描述
原位置 = 原位置 + oldCap

  • 说明: 5是假设计算出来的原来的索引值,这样就验证了函数所描述的,扩容之后所以节点要么就在原来的位置,要么就是被分配到了‘原位置 +旧容量’位置

  • 因此我们在扩容hashmap的时候,不需要重新计算hash值,只需要看看原来的hash值新增的那个bit是1还是0就可以了,
    (0表示索引没有变化,1表示原索引 + 旧容量)

在这里插入图片描述

正是因为这种巧妙的rehash方式,既省去了重新计算hash值的时间,而且同时,由于新增的1bit 是 0还是1
这是随机的,在reszie的过程中保证了rehash之后的每个桶上的结点数一定小于等于原来桶上的节点数,保证了rehash之后不会出现更加严重的hash冲突,均匀的把之前的冲突的节点分散到新的桶中了。

初始化map注意:

HashMap 的扩容机制,就是当达到扩容条件时会进行扩容。HashMap 的扩容条件就是当 HashMap 中的元素个数(size)超过临界值(threshold)时就会自动扩容。所以,如果我们没有设置初始容量大小,随着元素的不断增加,HashMap 会有可能发生多次扩容,而 HashMap 中的扩容机制决定了每次扩容都需要重建 hash 表,是非常影响性能的。

关于设置 HashMap 的初始化容量大小:

可以认为,当我们明确知道 HashMap 中元素的个数的时候,把默认容量设置成 initialCapacity/ 0.75F + 1.0F 是一个在性能上相对好的选择,但是,同时也会牺牲些内存。

而 Jdk 并不会直接拿用户传进来的数字当做默认容量,而是会进行一番运算,最终得到一个 2 的幂。

实例:

  • initalCapacity = (需要存储的元素的个数 / 负载因子) + 1

  • 负载因子默认是 0.75 ,建议暂时无法确定大小则一般设置为16

  • 如果不一开始指定初始化因子。需要放置1024个元素的时候,随着元素的不断增加,就需要扩容7次,重新建立hash表,严重的影响性能。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/150159.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 用命令行进入目录_在命令行如何进入子目录

    用命令行进入目录_在命令行如何进入子目录CD命令是更改目录命令如果要进入D盘不用这个命令直接输入D:回车即可要是你非要使用CD命令那要加参数/D你图中输入的CD D:系统只是认为你想在系统中记忆一下D盘所以还是返回原先目录例:D盘下有一个目录叫AD下面还有一个目录叫AE 我想在你图中的位置直接进入AE目录命令如下CD/DD:\AD\AE一定要加参数(/D)如果不加参数只写CDD:\AD\AE系统还是…

    2022年10月15日
    0
  • linux修改文件没有权限设置,Linux下修改文件权限(所有权)

    linux修改文件没有权限设置,Linux下修改文件权限(所有权)Linux与Unix是多用户操作系统,所以文件的权限与所有权的实现就显得很有必要;每个文件主要与三组权限打交道,分别是用户(user),用户组(group),其他用户(other)用户(u)是文件的所有者,通常有所有的文件的操作权限用户组(g)是多个用户的集合,可能有文件的部分访问权,相当于各用户之间的共享文件其他(o)是指文件所有者和用户组成员之外的任何人使用ls-l可以显示出当前目录下的文件…

    2022年9月12日
    0
  • 视频编解码优化的几个概念[通俗易懂]

    视频编解码优化的几个概念[通俗易懂]视频编解码1.neon2.gpu加速3.汇编neon在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速。目前在移动上使用最多的是ARM芯片。ARM是微处理器行业的一家知名企业,其芯片结构有:armv5、armv6、armv7和armv8系列。芯片类型有:arm7、arm9、arm11、cortex系列。指令集有:armv5、armv6和neon指令。关于ARM到知识参考:ht

    2022年7月15日
    21
  • [图文教程] 手把手教你安装Android SDK

    [图文教程] 手把手教你安装Android SDK配置环境总是痛苦的,不断地找教程,寻方法……在不断犯错的道路上跌跌撞撞……有点收获还好但是!几百年不配置一次环境,要这经验值何用?记录下来吧,以后也可以傻瓜式跟着教程走我已经下载并安装了AndroidStudio没有下载安装的可移步————>AndroidStudio官网下载开始下载AndroidSDK不用跑了——》AndroidSDK免费下载安装地址不让改不是…下一步吧安装权限……好像问题不大,下一步安装位置……可以更改…

    2022年7月21日
    11
  • [MFC]同步对象——CCriticalSection临界区,CSemaphore信号量

    [MFC]同步对象——CCriticalSection临界区,CSemaphore信号量实例——CCriticalSection临界区临界区是保证在某一个时间只有一个线程可以访问数据的方法。使用它的过程中,需要给每个线程提供一个共享的临界区对象,无论哪个线程占有临界区对象,都可以访问受到保护的数据,这时候其他的线程需要等待,直至该线程释放临界区对象为止,临界区被释放后,另外的线程可以强占这个临界区,以便访问共享的数据。临界区对应的一个CCriticalSection对象,

    2022年7月20日
    16
  • 编程语言中,取余和取模的区别

    编程语言中,取余和取模的区别编程语言中,取余和取模的区别

    2022年4月24日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号