HashMap底层原理

简介: 1. 基本概念2. HashMap 的底层数据结构3. HashMap 的 put 方法流程4. 怎么计算节点存储的下标5. Hash 冲突1)概念2)解决 hash 冲突的办法开放地址法再哈希法链地址法建立公共溢出区6. HashMap 的扩容机制1)扩容时涉及到的几个属性2)扩容的条件3)扩容的简要流程

1. 基本概念

HashMap 是基于 Map 接口实现的一个存储键值对数据的集合

最多允许一个为 null 的 key 值,且 HashMap 存储的数据是无序的


2. HashMap 的底层数据结构

HashMap 底层是由 数组 + 链表 / 红黑树 组成,当链表过长,会影响 HashMap 的性能,链表就会转变成红黑树,数组是 HashMap 的主体,数组中存储链表或红黑树的头节点


链表转换成红黑树的前提条件:

  • 当链表长度超过 8 并且数组长度超过 64 才会转换成红黑树
  • 链表转变为红黑树之前会进行判断,如果数组长度小于 64,那么会继续对数组扩容,而不是转变成红黑树


3. HashMap 的 put 方法流程

简要流程如下:


判断数组是否为空,是空就调用 resize 对数组初始化


根据 key 值计算 hash 值,得到该节点对在数组中存储的下标


如果没发生哈希冲突将该节点直接存入对应的数组下标处


冲突后先判断当前 Map 中 key 值是否存在,如果存在直接替换当前 Map 中 key 对应的 value


如果 key 值不存在且冲突节点处是红黑树,直接将该节点存入树上


如果 key 值不存在且冲突节点处是链表:


判断链表长度是否大于 8

小于 8 直接存入链表,

如果大于 8,就判断数组长度是否大于 64,大于 64 链表转换成红黑树,将该节点存入树上;小于 64 先对数组进行扩容,再重新计算 hash 值进行存储


43.jpg


4. 怎么计算节点存储的下标

先通过源码看一下 JDK 8 中是如何计算 hash 值的


44.png


先判断 key 值是否为 null,为 null 直接返回 0

如果不为 null,先调用 hashCode 方法得到 hashCode

将得到的 hashCode 按位右移 16 位,再与原来的 hashCode 进行异或操作得到 hash 值

计算下标:


通过 HashMap 存储节点的源码可以看出,HashMap 将节点存储在 (table.length - 1) & hash 下标处


即节点下标是数组长度减一再按位与 hash 值得到的


45.png


5. Hash 冲突

1)概念

hash 冲突是指,通过 hash 函数得到的存放节点的地址已经被占用了


2)解决 hash 冲突的办法

解决 hash 冲突的方法有:开放定址法、再哈希法、链地址法、建立公共溢出法。HashMap 中采用的是 链地址法


开放地址法

如果出现了冲突,就以上次得到的 hash 值再次进行运算得到一个新的 hash 值,直到找到一个没有冲突的 hash 值


再哈希法

提供多个不同的 hash 函数,如果发生了冲突,就用其他 hash 函数计算 hash 值,直到一个没有冲突的 hash 值


链地址法

将 hash 值相同的元素构成一个单链表,并将单链表的头指针存入哈希表中。此方法就是 HashMap 的底层数据结构,数组加链表的形式


建立公共溢出区

将哈希表分为公共表和溢出表,凡是发生冲突的数据统一放在溢出区


6. HashMap 的扩容机制


1)扩容时涉及到的几个属性

capacity :容量,默认 16

loadFactor:负载因子,默认是 0.75

threshold:阈值,阈值 = 容量 * 负载因子,默认是 12


2)扩容的条件

链表长度大于 8,数组长度小于 64

HashMap 中的元素个数大于阈值


3)扩容的简要流程

创建一个容量更大的数组,一般为原来数组的两倍,将原来数组的元素拷贝到新的数组中,扩容完成之后,阈值也变为原来的两倍


目录
相关文章
|
8月前
|
存储 缓存 安全
面试题-HashMap底层原理与HashTable的区别
字节跳动面试题-HashMap底层原理与HashTable的区别
76 0
|
8月前
|
存储 算法 Java
【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(下)
在阅读了上篇文章《【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(上)》之后,相信您对HashMap的基本原理和基础结构已经有了初步的认识。接下来,我们将进一步深入探索HashMap的源码,揭示其深层次的技术细节。通过这次解析,您将更深入地理解HashMap的工作原理,掌握其核心实现。
67 0
【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(下)
|
存储 算法 安全
HashMap底层实现原理
HashMap底层实现原理
170 0
|
19天前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
67 13
|
2月前
HashMap原理
1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的,特点是,key不能重复,可以为null,线程不安全 2.HashMap的扩容机制: HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
34 2
|
2月前
|
存储 Java 索引
HashMap原理详解,包括底层原理
【11月更文挑战第14天】本文介绍了数据结构基础,重点讲解了哈希表的概念及其实现方式,包括数组与链表的特点及其在HashMap中的应用。详细分析了Java 7及Java 8版本中HashMap的底层存储结构,特别是Java 8中引入的红黑树优化。此外,还探讨了哈希函数的设计、哈希冲突的解决策略以及HashMap的重要方法实现原理,如put、get和remove方法,最后讨论了HashMap的容量与扩容机制。
|
3月前
|
机器学习/深度学习 算法
让星星⭐月亮告诉你,HashMap之tableSizeFor(int cap)方法原理详解(分2的n次幂和非2的n次幂两种情况讨论)
`HashMap` 的 `tableSizeFor(int cap)` 方法用于计算一个大于或等于给定容量 `cap` 的最小的 2 的幂次方值。该方法通过一系列的无符号右移和按位或运算,逐步将二进制数的高位全部置为 1,最后加 1 得到所需的 2 的幂次方值。具体步骤包括: 1. 将 `cap` 减 1,确保已经是 2 的幂次方的值直接返回。 2. 通过多次无符号右移和按位或运算,将最高位 1 后面的所有位都置为 1。 3. 最终加 1,确保返回值为 2 的幂次方。 该方法保证了 `HashMap` 的数组容量始终是 2 的幂次方,从而优化了哈希表的性能。
37 1
|
4月前
|
设计模式 安全 Java
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
假如有T1、T2两个线程同时对某链表扩容,他们都标记头结点和第二个结点,此时T2阻塞,T1执行完扩容后链表结点顺序反过来,此时T2恢复运行再进行翻转就会产生环形链表,即B.next=A;采用2的指数进行扩容,是为了利用位运算,提高扩容运算的效率。JDK8中,HashMap采用尾插法,扩容时链表节点位置不会翻转,解决了扩容死循环问题,但是性能差了一点,因为要遍历链表再查到尾部。例如15(即2^4-1)的二进制为1111,31的二进制为11111,63的二进制为111111,127的二进制为1111111。
HashMap底层原理:数据结构+put()流程+2的n次方+死循环+数据覆盖问题
|
8月前
|
Java
HashMap原理解析
HashMap原理解析
172 47