HashMap底层结构、扩容机制实战探索

简介: HashMap底层结构、扩容机制实战探索

1.存储结构

从结构上,HashMap是由 数组+链表+红黑树(JDK1.8增加了红黑树部分) 实现的。


下图中,每一个黑色原点代表一个键值对(Node,实现了Map.Entry),table的默认长度是16;


JDK1.8引入了红黑树,链表长度大于8时转化为红黑树,极大地优化了HashMap的性能


2.获取hash数组索引位置

实质上有3步:取key的hashcode值,高位运算、取模运算


取模运算是JDK7里面的,JDK8没有这个运算方法:

h & (length - 1)


3.hash碰撞

有时候,如果2个key定位到了相同的位置,表示发生了hash碰撞

4.扩容机制(如何减少hash碰撞的概率)

想要减少hash碰撞,就要在键值对达到一定数量的时候,进行hash运算和扩容;


threshold是HashMap所能容纳的最大键值对(Node)数量,计算方式:


threshold = length * (1+loadFactor)


其中,


length是table的默认长度,值为16;


loadFactor是负载因子,默认值为0.75;


也就是说,初始化一个HashMap的时候,HashMap的极限值threshold的默认长度是16 * 1.75 = 12。从下图也可以看出来确实是12!


如上图所示,当前键值对已经达到极限值12,再向map中添加一个键值对之后,就要扩容了。

扩容后的length是原来length的2倍!

5. 设置HashMap的初始大小和负载因子

HashMap有一个构造方法可以设置初始大小和负载因子:HashMap(int initialCapacity, float loadFactor)

当然,也有一个构造方法HashMap(int initialCapacity),只设置初始大小,实际上也是调用了上面的构造方法:


扩容是一个非常消耗性能的操作,所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效的提高hashmap 的性能。比如说,我们有 1000 个元素 new HashMap(1000), 但是理论上来讲 new HashMap(1024) 更合适,不过上面 annegu 已经说过,即使是 1000,hashmap 也自动会将其设置为 1024。 但是 new HashMap(1024) 还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让 0.75 * length > 1000(length是table的长度), 我们必须这样 new HashMap(2048) 才最合适,既考虑了 & 的问题,也避免了 resize的问题。


6.一般用什么作为HashMap的key?

一般用Integer、String这种不可变类当HashMap当key,而且String最为常用。选择Integer,String这种不可变的类型,像对String的一切操作都是新建一个String对象,对新的对象进行拼接分割等,这些类已经很规范的覆写了hashCode()以及equals()方法。作为不可变类天生是线程安全的


7.HashSet底层是基于HashMap实现的

查看HashSet的源码可以知道,无论是构造方法,还是add、remove、size等方法,都是基于HashMap来实现的!把元素当做HashMap的key来存储,这是一个非常精巧的构思!

 

 

8.链表转换为红黑树,和树转换为链表的条件

1.链表转红黑树,两个条件,必须同时满足两个条件才能进行转换


条件1:单个链表长度大于等于8


条件2:hashMap的总长度大于64个、且树化的节点位置不能为空


2.红黑树转链表,两个条件,必须同时满足两个条件才能进行转换


条件1:树内节点数小于等于6


条件2:根节点为空,根节点的左右子树为空,根节点的左子树的左子树为空

相关文章
|
22天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
50 5
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5
|
1月前
|
存储
HashMap扩容机制
【10月更文挑战第11天】 `HashMap`的扩容机制是其重要特性之一。当容量达到负载因子(默认0.75)时,会触发扩容。扩容时,新容量通常是原容量的两倍,元素需重新哈希并迁移到新数组中。此过程涉及大量计算和迁移,可能影响性能。合理设置初始容量和负载因子,可减少不必要的扩容。在多线程环境中,还需注意线程安全问题。
|
1月前
|
算法 索引
让星星⭐月亮告诉你,HashMap的resize()即扩容方法源码解读(已重新完善,如有不足之处,欢迎指正~)
`HashMap`的`resize()`方法主要用于数组扩容,包括初始化或加倍数组容量。该方法首先计算新的数组容量和扩容阈值,然后创建新数组。接着,旧数组中的数据根据`(e.hash & oldCap)`是否等于0被重新分配到新数组中,分为低位区和高位区两个链表,确保数据迁移时的正确性和高效性。
64 3
|
1月前
|
算法 索引
HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导
HashMap在扩容时,会创建一个新数组,并将旧数组中的数据迁移过去。通过(e.hash & oldCap)是否等于0,数据被巧妙地分为两类:一类保持原有索引位置,另一类索引位置增加旧数组长度。此过程确保了数据均匀分布,提高了查询效率。
37 2
|
1月前
|
存储 算法 安全
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
HashMap常见面试题:红黑树、散列表,HashMap实现原理、扩容机制,HashMap的jd1.7与jdk1.8有什么区别,寻址算法、链表何时升级为红黑树、死循环
|
6月前
|
存储 安全 算法
【JAVA】HashMap扩容性能影响及优化策略
【JAVA】HashMap扩容性能影响及优化策略
|
3月前
|
Java 索引
【Java集合类面试九】、介绍一下HashMap的扩容机制
HashMap的扩容机制包括初始容量16,以2的次方进行扩充,使用负载因子0.75判断是否扩容,以及链表长度达到阈值时转换为红黑树,以优化性能。
【Java集合类面试九】、介绍一下HashMap的扩容机制
|
2月前
|
存储 算法 Java
深入剖析HashMap:理解Hash、底层实现与扩容机制
【9月更文挑战第6天】在Java编程中,`HashMap`是一个常用的数据结构,其高效性和可靠性依赖于深入理解哈希、底层实现及扩容机制。哈希通过散列算法将键映射到数组索引,采用链表或红黑树处理冲突;底层实现结合数组与链表,利用2的幂次方长度加快定位;扩容机制在元素数量超过负载因子与数组长度乘积时触发,通过调整初始容量和负载因子可优化性能。
|
3月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
57 0