Java进阶:HashMap底层原理(通俗易懂篇)
1.底层结构
Java 7及之前版本
在Java 7及之前的版本中,HashMap的底层数据结构主要是数组加链表。具体实现如下:
- 数组:HashMap的核心是一个Entry数组(
Entry<K,V>[] table),这个数组的大小总是2的幂。每个数组元素是一个单一的Entry节点,或者是一个链表的头节点。

- 链表:当两个或更多的键经过哈希运算后映射到数组的同一个索引上时,就会形成链表。Entry节点包含了键值对以及指向下一个Entry的引用,以此来解决哈希冲突。

Java 8及之后版本
从Java 8开始,HashMap的底层结构除了数组加链表之外,还引入了红黑树,以优化在链表过长时的查找性能。结构变为数组加链表加红黑树:
- 数组:同样是一个Entry数组(Java 8中称为Node),大小仍然是2的幂,用于快速定位。
- 链表:在哈希冲突时,键值对仍会以链表形式链接在一起。但与Java 7不同的是,Java 8对链表的处理增加了转换为红黑树的条件。
- 红黑树:当一个桶中的链表长度超过8且HashMap的容量大于64时(不大于64会先对数组进行扩容
resize()),链表会被转换成红黑树。这种转换提高了在大量哈希冲突情况下的查找效率,因为红黑树的查找时间复杂度为O(log n),相较于链表的O(n)有显著提升。

2.数据插入
在JDK1.7的时候,采用的是头插法

在JDK1.8改成了尾插法,这是因为头插法在多线程环境下扩容时可能会产生循环链表问题
线程不安全
无论是JDK1.7还是1.8都是线程不安全的,下图是1.8中的put方法

tab是就是HashMap的数组table,第一个if判断时做了赋值。框起来的部分表示如果没有hash冲突就直接在数组上插入元素,但是如果两个线程hash一样且都进入到了这个if下,线程1先执行的插入数据,线程2会覆盖1插入的数据。
那么什么是循环链表问题呢?这就不得不介绍一下HashMap的扩容机制了。
3.扩容机制
首先讲几个HashMap的属性
- table:数组,存放链表或红黑树的头节点
- Node:节点,属性有hash、key、value、next(下一个节点)
- size:元素个数,即节点node个数,非数组长度
- Capacity:当前数组长度
- loadFactor:加载因子,默认为0.75f,简称loadFactor
- threshold:扩容门槛,值为capacity*loadFactor,size达到这个门槛就扩容
当size大于threshold时,就会进行扩容resize()。
扩容分为两步
- 创建一个新的数组,长度为原数组的两倍
- 遍历所有Node节点,重新计算index值(Java8首先会重新计算hash值),放到新数组里,存在hash冲突的就放到链表或红黑树

为什么要重新计算index值,直接把张三这条链表复制到新的数组中不行吗?
答案是不行的,因为index规则是根据数组长度来的,如图

所以index 的计算公式是这样的:
- index = HashCode(key) & (Length - 1)
4.循环链表问题
循环链表问题理解起来比较麻烦,如果理解不了就知道JDK1.7HashMap扩容的时候有这么回事就行。但是可能是我自己太笨了万一大家一看就懂了呢
我们来看一下Java1.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];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
//重新计算元素在数组中的索引
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
重点在于transfer方法,作用是重新计算index值然后将旧数组中的数据迁移到新数组

循环链表的产生:
原因:
假设我们有两个Thread都在执行resize方法,Thread1第一步刚执行完第23行Entry<K,V> next = e.next;就卡住了,这时Thread2执行完了resize方法。
过程:
- Thread1第一次执行完Entry<K,V> next = e.next后,e=张三,next=李四,也就是说第二步执行李四的插入
- Thread2执行完resize后,李四的next变成了张三
- 此时又回到Thread1,第二次执行Entry<K,V> next = e.next后,e=李四,next=张三,也就是说又要执行张三的插入,循环链表产生!
由此我们知道了循环链表产生的关键在于头部插入元素A时,元素A的next元素B的next是元素A本身,所以Java8采用了尾插法,避免了循环链表。
求赞!求关注!!以后会更新更多有用的内容!!!꒰⑅•ᴗ•⑅꒱
保佑大家代码永无bug ╰(´︶`)╯
Java进阶:HashMap底层原理(通俗易懂篇)的更多相关文章
- HashMap底层原理分析(put、get方法)
1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...
- 最简单的HashMap底层原理介绍
HashMap 底层原理 1.HashMap底层概述 2.JDK1.7实现方式 3.JDK1.8实现方式 4.关键名词 5.相关问题 1.HashMap底层概述 在JDK1.7中HashMap采用的 ...
- [转]java 的HashMap底层数据结构
java 的HashMap底层数据结构 HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-v ...
- 面试官再问你 HashMap 底层原理,就把这篇文章甩给他看
前言 HashMap 源码和底层原理在现在面试中是必问的.因此,我们非常有必要搞清楚它的底层实现和思想,才能在面试中对答如流,跟面试官大战三百回合.文章较长,介绍了很多原理性的问题,希望对你有所帮助~ ...
- 详解 Java 8 HashMap 实现原理
HashMap 是 Java 开发过程中常用的工具类之一,也是面试过程中常问的内容,此篇文件通过作者自己的理解和网上众多资料对其进行一个解析.作者本地的 JDK 版本为 64 位的 1.8.0_171 ...
- Java面试& HashMap实现原理分析
1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O( ...
- hashMap 底层原理+LinkedHashMap 底层原理+常见面试题
1.源码 java1.7 hashMap 底层实现是数组+链表 java1.8 对上面进行优化 数组+链表+红黑树 2.hashmap 是怎么保存数据的. 在hashmap 中有这样一个结构 ...
- 【JAVA】HashMap的原理及多线程下死循环的原因
再次翻到以前工作中遇到的一个问题,HashMap在多线程下会出现死循环的问题,以前只是知道会死循环,导致CPU100%把机器拖跨,今天来彻底看看 首先来看下,HashMap的原理:HashMap是一个 ...
- HashMap底层原理
原文出自:http://zhangshixi.iteye.com/blog/672697 1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射 ...
- 1.Java集合-HashMap实现原理及源码分析
哈希表(Hash Table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常 ...
随机推荐
- golang写日志函数
package common import ( "bufio" "fmt" "os" "time" ) /*自定义日志文 ...
- SQLServer统计监控SQL执行计划突变的方法
使用动态管理视图(DMVs)来检测SQL执行计划的突变,你需要关注那些能够提供查询执行统计和计划信息的视图.以下是一些可以用于此目的的DMVs以及相应的查询示例: sys.dm_exec_query_ ...
- ES 2024 新特性
ECMAScript 2024 新特性 ECMAScript 2024, the 15th edition, added facilities for resizing and transferrin ...
- pageoffice6在线编辑word 文件禁止鼠标右键
有时让用户使用PageOffice只读模式(OpenModeType.docReadOnly)打开Word文件后,为了更好的只读效果,还希望禁用Word中的右键菜单,实现此效果只需创建com.zhuo ...
- Java类加载过程&&静态代码块的初始化过程
问题的引入 还是老规矩,先说说自己遇到的问题. 最近看到了一个比较有意思的Java程序,初次看到这段程序执行的结果还是挺让我意外的,话不多说先上程序,大家也可以揣摩一下(大神自行略过......) c ...
- Android Media Framework - 开篇
前言 Android Media是一块非常庞大的内容,上到APP的书写,中到播放器的实现.封装格式的了解,下到编解码组件的封装.VPU API的了解,每块内容的学习都需要我们下很大的功夫.此外,我们还 ...
- Android 12(S) Binder(三)
学以致用,这一节来native binder实战! android 12中的service用到的Bp.Bn文件多由aidl生成,所以实战中也用aidl来生成. 1.文件目录结构 文件目录结构如上,偷懒 ...
- FFmpeg Batch AV Converter 2.2.2 官方版
基本简介 FFmpeg Batch AV Converter官方版是一款Windows FFmpeg用户的前端程序,FFmpeg Batch AV Converter最新版允许使用FFmpeg命令行的 ...
- Qt-qrencode开发-生成、显示二维码📀
Qt-qrencode开发-生成二维码 目录 Qt-qrencode开发-生成二维码 1.概述 2.实现效果 3.编译qrencode 4.在QT中引入编译为静态库的QRencode 5.在Qt中直接 ...
- 关于《Java并发编程之线程池十八问》的补充内容
一.写在开头 在上一篇文章我们写<Java并发编程之线程池十八问>的时候,鉴于当时的篇幅已经过长,很多内容就没有扩展了,在这篇文章里对一些关键知识点进行对比补充. 二.Runnable v ...