为什么HashMap的数组长度是2的幂

简介: 为什么HashMap的数组长度是2的幂

为什么HashMap的长度一定是2的次幂呢?


今天和朋友聊天被问到HashMap的数组长度为什么是2的倍数。说实话挺惭愧的,秋招结束了,还不能完整的给出一个完整的答案。


今天和朋友聊天被问到HashMap的数组长度为什么是2的倍数。说实话挺惭愧的,秋招结束了,还不能完整的给出一个完整的答案。


我知道了HashMap的数据结构,也知道了什么是Hash冲突,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。


对于第一个问题针对的是hashmap数组扩容时,新数组length = 原数组length * 2,沿用前面的例子(array.length = 2^4 = 16,二进制10000),array.length 乘以 2 ,即二进制左移一位,由 10000 变成 100000。此时需要重新计算数组槽中的元素位置,如果槽中是链表,链表中每个元素都需要重新计算位置(这里不考虑红黑树)。t同时,由于get时需要对链表其进行遍历,链表越长检索效率越差。那么,计算出的key值落点越平均,hash冲突的可能性越小。key值的落脚点为key的hash值与数组长度作取余操作,记作key.hashcode % array.length。


数学角度考虑,保持array.length为质数会使得计算结果更均衡,hashTable就是这么做的(数组初始值11)。但 hashmap 中 array.length 偏偏选择了2的次幂,是个合数.完全出于性能考虑!


结论:当 array.length长度是2的次幂时,key.hashcode % array.length等于key.hashcode & (array.length - 1)。


以长度为16 , key值为 10011011001 举例,也就是


array.length = 16 , 二进制为:10000


array.length - 1 = 15 , 二进制为 : 1111


key.hashcode % array.length : 1001


key.hashcode & array.length-1 : 1001


计算过程:


100 1101 1001


& 1111


1001


发现两者计算的结果是一样的。


最终计算的结论就是:


10011011001 & ( 10000 - 1 ) = 10011011001 & 1111 = 1001 = 10011011001 % 10000

2554cf1fe44248b4ace60adc475ddff8.png


总结:


对hashmap而言,数组长度为2次幂有两点好处:

& 代替 %,可以提升性能

数组扩容时,仅仅关注 “特殊位” 就可以重新定位元素

仅关注 “特殊位” 就可以重新定位元素

性能,性能,还是性能……

相关文章
|
4月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
78 0
|
4月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
127 0
|
4月前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
52 0
|
网络协议 Java 数据库连接
HashMap源码手写简易篇(数组+链表)
HashMap源码手写简易篇(数组+链表)
|
存储 Java 开发者
复杂数据类型数组、队列HashMap、泛型 | 学习笔记
快速学习复杂数据类型数组、队列HashMap、泛型,课程揭秘Java面向对象编程的奥秘,深入解析Java经典数据类型,多线程编程详解,带你全面探索多线程编程的世界,攻克Java面向对象编程疑难点!
复杂数据类型数组、队列HashMap、泛型 | 学习笔记
|
2月前
|
Java
让星星⭐月亮告诉你,HashMap中保证红黑树根节点一定是对应链表头节点moveRootToFront()方法源码解读
当红黑树的根节点不是其对应链表的头节点时,通过调整指针的方式将其移动至链表头部。具体步骤包括:从链表中移除根节点,更新根节点及其前后节点的指针,确保根节点成为新的头节点,并保持链表结构的完整性。此过程在Java的`HashMap$TreeNode.moveRootToFront()`方法中实现,确保了高效的数据访问与管理。
32 2
|
2月前
|
Java 索引
让星星⭐月亮告诉你,HashMap之往红黑树添加元素-putTreeVal方法源码解读
本文详细解析了Java `HashMap` 中 `putTreeVal` 方法的源码,该方法用于在红黑树中添加元素。当数组索引位置已存在红黑树类型的元素时,会调用此方法。具体步骤包括:从根节点开始遍历红黑树,找到合适位置插入新元素,调整节点指针,保持红黑树平衡,并确保根节点是链表头节点。通过源码解析,帮助读者深入理解 `HashMap` 的内部实现机制。
42 2
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
67 0
|
1天前
|
存储 缓存 Java
HashMap源码剖析-put流程
更好地掌握 `HashMap` 的内部实现原理,提高编写高效代码的能力。掌握这些原理不仅有助于优化性能,还可以帮助解决实际开发中的问题。
30 13