为什么HashMap在Java中扩容采用2的n方倍?


Java中的HashMap在设计时选择以2的幂次方来扩大容量,这是出于性能效率方面的考虑。

1.提高运算速度

在HashMap中,元素的位置是通过计算其键的哈希码并将其映射到内部数组的一个索引来确定的。这个索引计算公式通常为:index = hash(key)&(capacity - 1)

capacity是HashMap的容量大小。这个公式利用了位运算中的按位与操作来计算索引,而不是取模运算(%),因为位运算通常更快。

当capacty是2的幂次方时,(capacity - 1)就是一个只有低位为1,其余位为0的数。在这种情况下,hash(key) & (capacty - 1)的效果等价于hash(key) %capacity,但前者更快,因为按位与操作在现代处理器上通常比取模运算要快得多。

  • 补充:

按位与运算: 如果有0则为0,都为1时则输出为1
HashMap默认容量为16,那么在存放到数组时就是n-1也就是15,而15二进制则是1111扩容后为32-1,即11111111,如果都为1的情况下是可以极大的减少hash碰撞,增加效率的

2.减少哈希碰撞

当HashMap的容量是2的幂次方时,哈希值的分布更加均匀,这有助于减少哈希碰撞的发生。

哈希碰撞是指不同的键映射到了同一个数组索引上。虽然不能完全避免哈希碰撞是,但可以通过合理选择容量大小可以最小化这种情况。

3.便于调整大小

当HashMap需要扩容时,新的容量也是2的幂次方,这使得旧的哈希值在新的容量下重新计算索引时,可以更好地分散到更大的数组中,从而进一步降低哈希碰撞的概率。


Java,`HashMap`是基于哈希表的线程非安全实现,它用来存储键值对,并且允许键为空(null)。以下是`HashMap`在Java的底层实现原理概述: ### 数据结构 - `HashMap`的基本单位是桶(Bucket)。在Java,桶是一个Node节点的数组。 - 每个Node包含一对数据:键(Key)和值(Value),以及指向下一个Node的引用(链接)。 ### 哈希函数 - `HashMap`使用键的hashCode()方法生成一个哈希值,然后对哈希表大小进行模运算得到桶的索引。 - 如果多个键产生相同的哈希值(哈希碰撞),`HashMap`采用链式探查(Linked List)来解决冲突。 ### 存储与搜索 - 当添加一个新的键值对时,首先计算键的哈希码,找到对应的桶。如果桶为空,创建一个新链表;若存在,则将新节点添加到链表末尾。 - 当搜索某个键时,同样使用它的哈希码定位到对应的桶,然后遍历链表查找目标键。 ### 扩容机制 - 当哈希表的负载因子(加载因子,默认为0.75)超过阈值时,`HashMap`会自动扩容。这时,它会创建一个新的、容量更大的哈希表,并重新将原有的元素按照新的哈希函数分配到新表。 - 扩容过程,旧的哈希表被复制到新的数组里,并清除一些临时数据。 ### 线程安全性 - Java 8及之后的版本引入了更强的线程同步策略。默认情况下,线程访问是不安全的,但如果需要线程安全的实现,可以通过使用`ConcurrentHashMap`替代。 ### 效率考量 - `HashMap`的平均时间复杂度为O(1),但由于哈希碰撞和扩容的影响,极端情况下性能可能下降到O(n)。 了解`HashMap`的工作原理有助于合理使用和优化它在应用程序的表现。记住适当调整负载因子和初始容量可以帮助避免不必要的扩张成本和提升性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值