JDK源码-HashMap-resize扩容方法(JDK7和JDK8)

集合系列(三) -HashMap详解 关于HashMap,一直都是一个非常热门的话题,只要你出去面试,我保证一定少不了它! 阅读详情

扩容操作可以认为是HashMap源码中最复杂的
resize方法的不是public的,不能通过client触发,所以只能直接进入源码查看

JDK 7

void resize(int newCapacity) {
	Entry[] oldTable = table;
	int oldCapacity = oldTable.length;
	if (oldCapacity == MAXIMUM_CAPACITY) {
		threshold = Integer.MAX_VALUE;
		return;
	}

	Entry[] newTable = new Entry[newCapacity];
	boolean oldAltHashing = useAltHashing;
	useAltHashing |= sun.misc.VM.isBooted() &&
			(newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
	boolean rehash = oldAltHashing ^ useAltHashing;//判断是否需要对原node重新hash定位table的index
	transfer(newTable, rehash); //扩容核心方法
	table = newTable;
	threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

进入 transfer 方法

void transfer(Entry[] newTable, boolean rehash) {
	int newCapacity = newTable.length;
	for (Entry<K,V> e : table) {//直接遍历table变量
		//链表跟table[i]断裂遍历,头部往后遍历插入到newTable中
		while(null != e) {
			Entry<K,V> next = e.next;
			if (rehash) {
				e.hash = null == e.key ? 0 : hash(e.key);
			}
			int i = indexFor(e.hash, newCapacity);
			e.next = newTable[i];
			newTable[i] = e;
			e = next;
		}
	}
}

扩容过程总结:
创建一个新的数组newTable,容量是oldTable的一倍
遍历oldTable,拿到每个链表
遍历链表,头插法插入newTable


JDK 8

final Node<K,V>[] resize() {
	Node<K,V>[] oldTab = table;
	int oldCap = (oldTab == null) ? 0 : oldTab.length;
	int oldThr = threshold;
	int newCap, newThr = 0;
	//一系列校验
	if (oldCap > 0) {
		if (oldCap >= MAXIMUM_CAPACITY) {
			threshold = Integer.MAX_VALUE;
			return oldTab;
		}
		else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && //容量翻倍
				 oldCap >= DEFAULT_INITIAL_CAPACITY)
			newThr = oldThr << 1; //扩容阈值也翻倍
	}
	else if (oldThr > 0) // initial capacity was placed in threshold
		newCap = oldThr;
	else {               // zero initial threshold signifies using defaults
		newCap = DEFAULT_INITIAL_CAPACITY;
		newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
	}
	if (newThr == 0) {
		float ft = (float)newCap * loadFactor;
		newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
				  (int)ft : Integer.MAX_VALUE);
	}
	threshold = newThr;
	@SuppressWarnings({"rawtypes","unchecked"})
		Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
	table = newTab;
	if (oldTab != null) {
		for (int j = 0; j < oldCap; ++j) {//开始遍历oldTable
			Node<K,V> e;
			if ((e = oldTab[j]) != null) {
				oldTab[j] = null;
				if (e.next == null) //table[i]没值
					newTab[e.hash & (newCap - 1)] = e;
				else if (e instanceof TreeNode) //table[i]是红黑树的结构
					((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
				else { //table[i]是链表的结构,扩容后还是保持顺序的
					Node<K,V> loHead = null, loTail = null; //“低链表”的头和尾,即扩容后的在new table的index和old index是一样的
					Node<K,V> hiHead = null, hiTail = null; //“高链表”的头和尾,即扩容后的在new table的index是old index的2倍
					Node<K,V> next;
					do {
						next = e.next;
						if ((e.hash & oldCap) == 0) {//newTable index ==  oldTable index
							if (loTail == null)
								loHead = e;//头只当初始时才赋值
							else
								loTail.next = e;//新插入的都到添加到tail中
							loTail = e;
						}
						else {//newTable index ==  oldTable index * 2
							if (hiTail == null)
								hiHead = e;//头只当初始时才赋值
							else
								hiTail.next = e;//新插入的都到添加到tail中
							hiTail = e;
						}
					} while ((e = next) != null);
					if (loTail != null) { //将“低链表”添加到newTable中
						loTail.next = null;
						newTab[j] = loHead;
					}
					if (hiTail != null) {//将“高链表”添加到newTable中
						hiTail.next = null;
						newTab[j + oldCap] = hiHead;
					}
				}
			}
		}
	}
	return newTab;
}

总结(先针对链表操作的总结,红黑树的后续文章会写):
如果是链表处理(红黑树的后续文章讨论),那么就将oldTable[i]分成高低链表,生成后插入到newTable中


对比

JDK7是每拿到一个Node就直接插入到newTable,而JDK8是先插入到高低链表中,然后再一次性插入到newTable。
所以链表的扩容过程JDK7会出现死循环问题,而JDK8避免了这个问题。
JDK8跟原先的链表对比Node之间顺序是一致的,而JDK7是是反过来的。

ComfyUI电影级AI视频生成:MiniMax Director Pro v1.2节点实战指南 AI视频生成正从简单的文生视频迈向电影级叙事控制,而ComfyUI作为节点化工作流平台,为创作者提供了精细化调度的可能。在ComfyUI生态中,MiniMax Director Pro v1.2以导演控制层角色,将分镜描述、镜头运动、角色关系拆分为可调参数,配合底层视频生成模型完成从提示词到电影片段的渲染。本文从AI视频生成的基本原理切入,介绍Director Pro v1.2与ComfyUI的组合价值,并围绕环境准备、节点安装、模型配置、工作流搭建及故障排查展开工程实践,帮助读者掌握可复用的电影级AI视频 阅读详情

相关推荐

jdk8之HashMap resize方法详解(深入讲解为什么1.8中扩容后的元素新位置为原位置+原数组长度)

缘起 在此之前,笔者对HashMap的工作原理进行过简单的解析, jdk8中的HashMap相对于jdk7有比较大的更新,本文主要是对改动后的resize()方法进行详细解析。看本文需要一定的HashMap基础,如果没有基础的同学建议先看笔者的另一篇文章 resize()方法的作用 resize()方法会在HashMap的键值对达到“阈值”后进行数组扩容,而扩容时会调用resize()方法,此外,...

诺浅的专栏 1万+

【大厂面试题系列】:JDK7、8 HashMap扩容原理源码讲解

文章目录前言JDK7 HashMap扩容JDK8 HashMap扩容 前言 JDK7 和JDK8 的扩容方法都基于 resize()方法,但底层实现却有所不同 JDK7 HashMap扩容 void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table

Code皮皮虾的博客 1140

【HCIE】FRR+BFD+OSPF与BGP联动

FRR+BFD+OSPF与BGP联动

Wenrs的博客 1995

【Java中HashMap必须掌握的基本知识】

【HashMap面试必须掌握的基础知识】HashMap的底层数据结构?HashMap中从获取对象的hash到散列计算规则? 默认初始化大小是多少?为啥大小都是2的幂?HashMap的主要参数都有哪些?哈希冲突基本概念以及HashMap解决冲突的方法为啥我们重写equals方法的时候需要重写hashCode方法呢?HashMap和Hashtable的区别是什么?什么是Java集合中的快速失败(fast-fail)机制?HashMap的工作原理?HashMap的table的容量如何确定?loadFactor是什

Coder_ljw的博客 674

【生成数据集EXCEL文件】使用生成对抗网络GAN生成数据集:输出生成数据集EXCEL

生成器的任务是接收随机噪声向量作为输入,并尽可能生成与真实数据相似的样本,而判别器则是一个二分类器,旨在区分输入的样本是来自生成器生成的,还是来自真实数据。在训练过程中,生成器和判别器通过对抗学习的方式相互博弈,生成器的目标是生成足以欺骗判别器的样本,而判别器的目标则是尽可能准确地判断样本的真实性。GAN网络在多个领域展现了强大的能力,能够生成与训练数据分布相似的新数据,包括但不限于图像生成、图像风格转换、超分辨率、数据增强、视频生成、自然语言处理、医学图像处理以及游戏与虚拟现实等。

hustef的博客 1521

将机器人urdf模型转换为Mujoco环境下的mjcf模型

在使用gym环境进行人形模型站立训练的过程中,我想要将之前ros环境下的机器人urdf模型导入到mujoco环境下,这时候就需要将urdf模型转换为mujoco专用的mjcf模型。-AzureLoong.urdf是机器人模型(我这里使用青龙的模型);--custom_robot.xml是转换完成之后的机器人模型的名字和格式;包要求 Python 版本必须是 3.11 或更高),保证自己的其他训练环境和依赖不被干扰。首先将meshes文件迁移到需要输出的目录下,然后再运行转换命令。最后就可以成功输出了。

weixin_44334573的博客 3754

深入理解Java集合之HashMap死循环

一、问题 由于HashMap并非是线程安全的,所以在高并发的情况下必然会出现问题, 如果是在单线程下使用HashMap,自然是没有问题的,如果后期由于代码优化,这段逻辑引入了多线程并发执行,在一个未知的时间点,会发现CPU占用100%,居高不下,通过查看堆栈,你会惊讶的发现,线程都Hang在hashMap的get()方法上,服务重启之后,问题消失,过段时间可能又复现了。 二、原因分析 HashMa...

qq_42499816的博客 791

如何使用C#自带的GDI+双缓冲类BufferedGraphics实现双缓冲功能

小白摸索着编写一个上位机界面,将遇到的问题及解决方法记录下,以供后来的小白使用哈。这篇文章解决的问题是在绘图时候的闪烁问题。其中网上有些介绍的设置控制方式controlstyle的方法,这个方法我的理解是比较适合于在一个完整的窗口当中绘图。如果要在一个窗口当中的内部容器或者控件内画图的话就不太适合,通常按照前述的方法设置后没有任何效果。对于在窗口内部的绘图的来说,比较有效的方法是在内存当中开辟一块

z20110411的博客 4794

java经典面试题之hashmap

经典面试题总结: hashmap具备哪些特点? Java7和Java8的区别? 哈希表如何解决Hash冲突? 为什么 HashMap 中 String、Integer 这样的包装类适合作为 key 键 HashMap 中的 key若 Object类型, 则需实现哪些方法? hash的计算规则? 默认初始化大小是多少?为啥...

breakout_alex的博客 437

一文搞懂雷达脉冲压缩和匹配滤波器

为了解决传统单频脉冲雷达面临的作用距离和空间分辨力之间的矛盾,脉冲压缩理论被提出。在接收端设计一个和发射信号能够“共轭匹配”的网络来实现脉冲压缩。接收到的回波信号通过匹配滤波器时,滤波器的增益在信号越强的频率点越大;在信号越弱的频率点,增益越小,通过这样的方式使信号在时域更加集中。脉冲压缩技术大致可分为线性调频(LFM)和相位编码。其中LFM信号易于通过硬件或各种技术产生,但是进行匹配滤波后主旁瓣比高达13.2dB,在检测多个目标时,强目标的回波旁瓣可能会把弱小信号覆盖,导致弱小信号的检测受到影响。

FPGA入门到跑路 9933

【二】MADDPG多智能体算法实现(parl)【追逐游戏复现】

论文全称:Multi-Agent Actor-Critic for Mixed Cooperative-Competitive Environments 论文原文:https://download.youkuaiyun.com/download/sinat_39620217/16203960 论文翻译:https://blog.youkuaiyun.com/qiusuoxiaozi/article/details/79066612 ...

丨汀、的博客 2万+

HashMap源码分析与常见八股

当链表的长度大于TREEIFY_THRESHOLD(默认为8)时会开始转化,同时要判断Node数组的个数是否大于MIN_TREEIFY_CAPACITY(默认64),如果小于就会先进行扩容,而不是转化,如果大于就直接转化为红黑树。时,会转化,如果当前数组长度小于64就学会先进行数组扩容,否则就转化为红黑树,默认大小为16,每次扩容2倍。拉链法就是使用一个链表数组,数组中的每一格都是一个链表,如果遇见哈希冲突,则将冲突的值加到链表中。可能是一个空元素,也可能是链表已存在,所以要区别对待。

m0_74047598的博客 2002

智能座舱SoC平台横评:SA8155P、SA8255P、SA8295P、MT8678、MT8628 谁是下代车机首选?

高通SA8295P以PC级架构、强悍AI算力,代表安卓阵营迈向“本地大模型+空间计算”阶段;联发科MT8678则凭借低功耗 + 高带宽,成为副驾多媒体/副屏智能化的性价比之王。在座舱设计中,SoC平台决定了你能给用户什么样的交互方式,选型时切忌只看跑分,而要回归“体验-热设计-平台生态”的三角平衡。

weixin_43199439的博客 3353

【Java集合】HashMap的resize()源码详解以及JDK1.7与JDK1.8的区别

HashMap的扩容源码详解,以及JDK1.7和JDK1.8的resize()方法的源码比较。

小七的博客 2754

数学之美:欣赏超越数e

文章目录一、e为何物?二、利息与e的关系1、利息问题2、计算利息3、欣赏螺旋线(1)螺旋线概述(2)绘制螺旋线三、e和π 一、e为何物? e是一个无限不循环小数,其值约等于2.718281828…,它是一个超越数,有时也称为欧拉数(Euler’s Number)。 以e为底的对数称为自然对数(Natural Logarithm),数学中使用自然这个词的还有自然数(Natural Number)。 这里的“自然”并不是现代人所习惯的“大自然”,而是有点儿“天然存在,非人为”的意思。 二、利息与e的关系

howard2005的专栏 1713

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

在JDK1.7和JDK1.8中HashMap中的扩容机制

在JDK1.7和JDK1.8中,针对HashMap扩容时原键值对在新数组中的位置问题,JDK8和JDK7其实是没有本质区别的

by_zed的博客 1965

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Java8的HashMap详解(含与之前版本hashmap的区别)

JDK1.8对HashMap底层的实现进行了优化,例如引入红黑树的数据结构和扩容的优化等。本文结合JDK1.7和JDK1.8的区别,深入探讨HashMap的结构实现和功能原理,文章末尾附有HashMap的put方法和resize方法的源码解析。 简介 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、Link...

weixin_38852633的博客 6476

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Java基础-源码分析-HashMap/HashTable/HashSet/LinkedHashSet

Java工程师知识树 / Java基础 HashMap/HashTable/HashSet/LinkedHashSet源码上区别概述: HashMap/HashTable HashMap、 Hashtable都是最常见的一些Map实现,是以键值对的形式存储和操作数据的容器类型。主要区别在于Hashtable在方法层面加上了synchronized。 HashSet/LinkedHashSet 而HashSet的底层是基于HashMap,所以HashSet的数据结构就是HashMap的数据结构,只是Ha

hughjin 460

Java学习笔记——HashMap底层源码分析

目录源码内一些参数介绍实例化(声明一个HashMap对象)注意事项添加键值对hash(key)源码分析putVaule()源码分析resize()源码分析扩容后重新计算键值对在数组中的位置JDK1.7与JDK1.8的区别插入值put扩容resize() 源码内一些参数介绍 capacity:HashMap的容量,即Node[]数组的size,HashMap会强制的将容量设置为2的幂,这样在对key取hash值的时候可以直接使用位运算来计算hash值。(比如容量为16时,只取key对象hash值的后4位,

qq_36334929的博客 3076

【八股学习】HashMap源码总结

使用默认的或者指定的初始化容量和负载因子,如果使用默认容量16,则会在第一次插入时在 resize 中自行计算 threshold。进行扩容,会伴随着一次重新 hash 分配,并且会遍历 hash 表中所有的元素,是非常耗时的。resize 方法实际上是将 table 初始化和 table 扩容 进行了整合,底层的行为都是给 table 赋值一个新的数组。3、最后放入到新的map中,如果是未初始化的table会在putVal中resize初始化扩容。2、初始化了则进行判断并resize。

messiy727的博客 1227

Java8的HashMap详解(存储结构,功能实现,扩容优化,线程安全,遍历方法)

文章转自:https://blog.youkuaiyun.com/login_sonata/article/details/76598675 JDK1.8对HashMap底层的实现进行了优化,例如引入红黑树的数据结构和扩容的优化等。本文结合JDK1.7和JDK1.8的区别,深入探讨HashMap的结构实现和功能原理,文章末尾附有HashMap的put方法和resize方法的源码解析。 简介 Java为数...

striner的博客 487

7.Java SDK源码分析系列笔记-JDK1.8 HashMap

1.1.Java SDK源码分析系列笔记-Integer2.2.Java SDK源码分析系列笔记-String系列3.3.Java SDK源码分析系列笔记-LinkedList4.4.Java SDK源码分析系列笔记-ArrayList5.5.Java SDK源码分析系列笔记-Vector6.6.Java SDK源码分析系列笔记-SynchronizedList7.7.Java SDK源码分析系列笔记-JDK1.8 HashMap8.

weixin_40316402的博客 94

java hashmap putall_Java基础:HashMap中putAll方法的疑惑

最近回顾了下HashMap的源码(JDK1.7),当读到putAll方法时,发现了之前写的TODO标记,当时由于时间匆忙没来得及深究,现在回顾到了就再仔细思考了下@Overridepublic void putAll(Map extends K, ? extends V>m) {int numKeysToBeAdded =m.size();if (numKeysToBeAdded == 0)...

weixin_42692935的博客 1085

HashMap 底层实现原理是什么?JDK8 做了哪些优化?

在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的,JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构,它的组成结构如下图所示: 数组中的元素我们称之为哈希桶,它的定义如下: static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<

Andrew.J 166

HashMap源码解析超详细

HashMap源码解析超详细

weixin_43691942的博客 1960

【JUC】HashMap源码JDK1.8与1.7的区别

【JUC】HashMap源码JDK1.8与1.7的区别1、HashMap2、HashMap底层数据结构2.1JDK1.8之前2.2 JDK1.8之后2.3 JDK1.7 VS JDK1.8 比较3、HashMap源码分析3.1 开始扒源码3.1.1 构造方法3.1.2 put方法3.1.3 resize方法3.1.4 treeifyBin()方法3.1.5 get方法3.1.6 remove()方...

aaa_bbb_ccc_123_456的博客 3221
上一篇: JDK源码-HashMap-remove方法(JDK7和JDK8)
下一篇: Flink-1.10.0 Native Kubernetes 部署方式实践
瓜牛呱呱
博客等级 码龄10年 36粉丝 59原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值