关于HashMap的十个灵魂拷问(含源码级解析)

一、为什么HashMap总被面试官翻牌子?

在Java技术岗面试中,HashMap的出镜率堪比顶流明星!!!据统计,95%的Java工程师在面试中都会被问到HashMap相关的问题。这货简直就是面试界的"钉子户",(悄悄说)甚至有些面试官开场就直接来一句:“先说说HashMap吧”。

二、底层结构到底藏着什么玄机?

先来张灵魂手绘图(想象一下):

数组 + 链表 → 数组 + 链表/红黑树(JDK8+)

每个数组元素我们称为桶(Bucket),每个桶里装的可能是:

  1. 单个节点(最理想情况)
  2. 链表(哈希冲突时)
  3. 红黑树(链表长度≥8且数组长度≥64时)

三、put方法执行时的暗箱操作

你以为put就是简单的存数据?太天真了!来看源码级的执行流程:

  1. 计算哈希值:不是直接用hashCode()!!!

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    

    这个扰动函数(重要!!!)能有效减少哈希碰撞

  2. 定位桶位置:(n-1) & hash (n是数组长度)

  3. 处理碰撞

    • 无节点:直接新建
    • 有节点:比较key(先比哈希值,再比equals)
  4. 树化检查:当链表长度≥8,且数组长度≥64时转红黑树

四、初始容量16是玄学吗?

(敲黑板)这个设计其实充满智慧:

  1. 2的幂次方特性:(n-1) & hash 等效于取模运算,但效率更高
  2. 默认负载因子0.75:空间和时间成本的折中方案
  3. 扩容阈值=容量×负载因子:12(当默认容量16时)

五、扩容机制里的骚操作

当size超过阈值时,会发生resize(这个操作耗时!!!)。JDK8的优化点:

newTab[e.hash & (newCap-1)] = e; // 原索引位置
newTab[e.hash & (newCap-1) + oldCap] = e; // 新索引位置

利用高位判断,节点要么在原位置,要么在"原位置+旧容量"的位置,不需要重新计算哈希!!!

六、线程安全问题有多可怕?

来看这个死亡现场:

// 线程A执行到此处
if ((p = tab[i = (n - 1) & hash]) == null)
    // 线程切换!!!
    tab[i] = newNode(hash, key, value, null);

此时线程B也执行相同操作,就会导致数据覆盖。更可怕的是链表可能成环(JDK7的bug),导致CPU飙升到100%!

七、为什么重写equals必须重写hashCode?

来看这个恐怖故事:

Map<Student, Integer> map = new HashMap<>();
Student s1 = new Student("张三", 20);
Student s2 = new Student("张三", 20);

map.put(s1, 100);
System.out.println(map.get(s2)); // 输出null!

这就是没同时重写hashCode的后果!因为默认的hashCode()是根据内存地址计算的。

八、加载因子能不能随便改?

官方建议:不要动我的0.75!这个值是统计学得出的黄金分割点:

  • 大于0.75 → 空间节省但哈希碰撞增加
  • 小于0.75 → 空间浪费但查询更快

特殊场景调整建议:

  1. 内存紧张 → 适当调高
  2. 查询频繁 → 适当调低

九、遍历时顺序不可靠?

这可不是bug是feature!HashMap的遍历顺序:

  1. 从数组的0号索引开始
  2. 逐个遍历每个桶
  3. 每个桶里按链表/树结构遍历

所以当发生扩容后,遍历顺序可能完全改变!(重要)

十、红黑树转换的隐藏条件

记住两个必要条件:

  1. 链表长度≥8
  2. 数组长度≥64(这个很多人会忘!!!)

否则只会触发扩容而不是树化。设计者考虑到在小容量时,扩容比树化更划算。

高频面试题集锦(背熟保命)

  1. 为什么用红黑树不用AVL树?
  2. 头插法和尾插法有什么区别?
  3. ConcurrentHashMap如何保证线程安全?
  4. 为什么建议用String/Integer作为key?
  5. 怎么设计一个不可变类作为key?

最后的小贴士

理解HashMap的最好方式是:

  1. 手动画出put过程(别偷懒!)
  2. 用IDEA的Debug模式单步跟踪
  3. 自己实现一个简化版HashMap(强烈推荐)

下次面试再被问HashMap,记得笑着对面试官说:“您想听哪个版本的实现?JDK7还是JDK8的?” (装X有风险,使用需谨慎)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值