面试官的最爱:HashMap 扩容原理你真的懂吗?

360影视 2024-12-29 21:54 3

摘要:这可是每个 Java 开发者都绕不开的基础问题,也是面试官爱考察的重点!不管你是职场小白,还是开发老手,如果对这个问题含糊不清,可能会在面试中被追问得哑口无言。

小伙伴们,今天我们来聊一个在社招面试中常见又经典的问题——HashMap的扩容操作是怎么实现的?

这可是每个 Java 开发者都绕不开的基础问题,也是面试官爱考察的重点!不管你是职场小白,还是开发老手,如果对这个问题含糊不清,可能会在面试中被追问得哑口无言。

那天,刚入职场的小林去参加一家知名互联网公司的面试。面试官面带微笑地问道:“小林,能不能简单说一下 HashMap 的扩容机制?”

小林想了想,说:“HashMap 扩容是为了防止哈希冲突过多。容量会翻倍,旧元素会重新分配到新的桶中。”

“嗯,不错。”面试官点了点头,接着追问:“那你知道具体是怎么实现的吗?比如,什么时候触发扩容?扩容后为什么不会导致数据丢失?”

小林有点慌了,只能支支吾吾地说:“我只知道……它会重新计算位置……”

这时,面试官的笑容明显收敛了一些。

那么,HashMap 的扩容到底是怎么回事?

接下来,我们以Java 8 的 HashMap为例,一步步带大家深入这个问题。

在了解扩容之前,先搞清楚 HashMap 的基本构造。它的核心部分包括以下几块:

数组(table):存储所有键值对的桶,初始大小是默认的 16。链表或红黑树:用于解决哈希冲突。桶中的每个元素其实是一个链表的头节点,当链表长度超过一定阈值(8)时会转为红黑树。负载因子(loadFactor):用来衡量装满程度的参数,默认是 0.75。容量(capacity):桶数组的长度,必须是 2 的幂。阈值(threshold):扩容的临界值,等于 capacity * loadFactor。

当 HashMap 中的元素数量超过 阈值 时,就会触发扩容操作。例如:

初始容量是 16,默认负载因子是 0.75。阈值 = 16 × 0.75 = 12。当元素数量达到 13 时,就需要扩容。扩容后,容量会变成当前容量的两倍,即 32。

1. 创建新数组

HashMap 会创建一个新桶数组,新数组的大小是旧数组的两倍。比如从 16 扩容到 32。

2. 重新计算哈希值

旧数组中的每个键值对会被重新分配到新数组中,重新计算其存放位置。

新的索引是通过公式计算的:

这里的 newCapacity 就是新桶数组的大小。

3. 分割链表

Java 8 引入了优化,当扩容时,如果桶中是链表(而不是红黑树),它会把链表分为两部分:

第一部分依然存储在原索引位置。第二部分存储在原索引 + oldCapacity 的位置。

我们来看一下关键代码:

重点是 transfer 方法,它完成了重新分配数据的过程:

每个节点都会被重新计算索引。数据转移后,链表的指针关系依然保持完整。数据转移是单线程完成的,没有中断风险。

如果链表已经转化为红黑树,扩容时会重新构造红黑树,以保证高效性。相比链表,红黑树的搜索效率更高,时间复杂度从 O(n) 降低到 O(log⁡n)。

小林离开面试后,总结了自己的不足,重新梳理了 HashMap 的扩容机制。后来,他又去了一家大厂面试,当被问到同样的问题时,他从触发条件、实现步骤到源码细节,讲得头头是道。面试官满意地点头:“不错,你对底层理解得很深!”

于是,小林顺利拿到了 offer。

这就是今天的分享!大家对 HashMap 的扩容还有什么疑问吗?或者你在面试中遇到过哪些棘手的问题,欢迎在评论区留言,我们一起探讨~

来源:思远教育

相关推荐