面试官:说说HashMap的底层结构?谢飞机:嗯...是个Map!

4 阅读3分钟

面试现场:严肃面试官 VS 搞笑水货程序员谢飞机

面试官:(面无表情)你好,先做个自我介绍吧。

谢飞机:(自信满满)面试官好!我叫谢飞机,精通 Java 全家桶,从 Hello World 到删库跑路,样样精通!尤其擅长在深夜修复自己白天写的 Bug!

面试官:(嘴角微微抽搐)...很好。那我们开始吧。你在简历里写熟悉集合框架,能说说 HashMap 的底层数据结构吗?

谢飞机:(秒答)这个简单!HashMap 嘛,它就是一个 Map!里面存的是 key 和 value!

面试官:(强忍住)...没错,但我想知道它的底层是怎么实现的?

谢飞机:(挠头)底层啊...哦!我知道了!它底层用的就是一个...一个超级大的数组!对,数组!所有东西都塞进去!

面试官:(扶额)那如果两个 key 算出来的数组位置一样怎么办?

谢飞机:(眼睛一亮)那就...叠起来放!像叠罗汉一样!实在叠不下了,就...就换个地方重新叠!

面试官:(深吸一口气)...行吧。今天的面试就到这里,你回去等通知吧。

谢飞机:(起身,充满希望)好的面试官!期待您的好消息!


技术点详解:HashMap 的底层原理

HashMap 是 Java 中最常用的数据结构之一,其核心是基于 数组 + 链表 + 红黑树 的设计。

  1. 核心结构:HashMap 内部维护了一个名为 table 的 Node 数组。Node 是一个内部类,包含了 key、value、hash 值以及指向下一个节点的引用(next)。
  2. 哈希计算与寻址:当我们调用 put(key, value) 方法时,HashMap 会先对 key 调用 hashCode() 方法得到一个哈希值,然后通过一个扰动函数((h = key.hashCode()) ^ (h >>> 16))进行二次哈希,以减少哈希冲突。最后,通过 (n - 1) & hash(n 是数组长度)来确定该键值对应该存放在数组的哪个索引位置上。
  3. 处理哈希冲突:当多个 key 计算出的索引位置相同时,就会发生哈希冲突。HashMap 采用 链地址法 来解决冲突。即,在同一个数组索引位置上,将所有冲突的节点用 next 指针连接起来,形成一个单向链表。
  4. 红黑树优化:在 JDK 1.8 之前,HashMap 只有数组和链表。当链表过长时(例如被恶意攻击构造大量冲突的 key),查询效率会从 O(1) 退化到 O(n)。为了解决这个问题,JDK 1.8 引入了红黑树。当链表的长度达到 8 并且数组的长度大于等于 64 时,链表会转换为红黑树,将查询时间复杂度从 O(n) 降低到 O(log n)。反之,当红黑树中的节点数少于 6 时,又会退化回链表。

所以,谢飞机说的“大数组”和“叠起来”其实歪打正着地描述了数组和链表的概念,但完全忽略了哈希算法、冲突解决机制以及红黑树这一关键优化。