HashMap每次扩容时,为什么都必须是2的N次方?

文章探讨了HashMap扩容时选择2的幂次方的原因,通过源码分析指出,这样做能保证键值对的hash值在扩容后均匀分布,减少碰撞并优化性能。扩容时新数组长度为旧长度的2倍,确保最大索引值的二进制形式利于分散元素,降低链表和红黑树转换概率。
摘要由CSDN通过智能技术生成

一. 背景介绍

最近有粉丝问壹哥,为什么HashMap每次扩容时,都必须是2的N次方?

其实要想弄明白这个问题,我们就必须知道HashMap的底层源码结构。接下来壹哥就带各位来分析一下HashMap的底层设计。

我们知道,HashMap的底层是通过数组+链表+红黑树的数据结构来存放数据的。当新添加元素的key值出现了hash碰撞,就会在同一bucket中形成链表或者红黑树当键值对的数量超过阈值时就会扩容,将以前处于同一个链表或者红黑树上的元素打散,在新数组的 bucket 上进行重新分布。

当HashMap在初始化没有指定容量的情况下,首次添加元素时,数组的容量为16;当超出阈值,数组容量为扩容为之前的2倍。

那么问题来了,为什么HashMap会将首次初始化容量设置为16,而后续每次扩容都是之前的2倍?而不是像ArrayList首次为10,后续为1.5倍呢?这可是我们在面试时的一个高频考点哦!壹哥提醒各位,一定要搞清楚这一点哦。

二. 问题解答

2.1 对应源码分析

其实要想回答出上面提出的问题,我们可以从HashMap的源码里找到答案,如下图所示:

其中 n 为数组的长度,n - 1 为数组的最大索引值(n - 1)& hash 的意思是将每个元素key的hash值与最大索引值进行相与操作。然后判断对应的 bucket 位置是否有元素,如果没有元素则在对应的 bucket 位置直接添加;如果有元素,则形成链表或者红黑树。

2.2 深入分析

各位看官,你现在可能对上面的内容还是有点云里雾里,别急,让我们再来看一组数据:

长度

最大索引

二进制数

16

15

1111

32

31

11111

64

63

111111

当数组初始长度为16的时候,每次扩容都为之前的2倍,那么就保证了每次扩容之后新数组的最大索引值对应的二进制数为全1根据2.1小节中,图片标识的 (n - 1) & hash,那么就能保证添加到HashMap中key的hash值与最大索引相与时,能够最大化的分散到HashMap所有的 bucket 中,进而最大化避免出现 hash碰撞而形成链表或者红黑树。

壹哥再反向地跟各位看官论证一下。假如说 HashMap的初始化长度是10,那么最大索引值为9,而9对应的二进制数是 1001。那么key的hash值与 9相与,结果只可能为 0、1、8、9,那么新增的数据永远只能放到数组索引为 0、1、8、9这四个位置,这就大大增加了出现链表和红黑树转换的概率。

所以初始化为16,每次扩容是之前的2倍,这就大大降低了链表和红黑树转换的概率,自然也就提高了HashMap的性能。现在你明白了吗?

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一一哥Sun

您的鼓励是我继续创作的动力哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值