HashMap初始化会做什么操作?

9 阅读2分钟

欢迎来到 “HashMap 地下城”

关于 HashMap 的初始化(也就是你执行 new HashMap<>() 的那一刻),这里其实隐藏着一个新手村的经典陷阱(宝箱怪)

很多初出茅庐的勇士以为,一旦执行了这句代码,系统就会立刻在内存中为你建造一座拥有 16 个房间的庞大迷宫(数组)。但实际上,在 Java 8 中,什么都没有建造!

这就是 HashMap 初始化的核心机制:懒加载(Lazy Initialization)

为了让你更清晰地打怪,我们把初始化分为两种常见的“出生方式”来看:

出生方式一:默认开局(无参构造)

当你使用 Map<String, String> map = new HashMap<>(); 时:

  • 系统操作:仅仅只是给你发放了一个“背包默认装载率”的属性。它会将内部的负载因子(loadFactor)赋值为默认值 0.75f
  • 迷宫状态:此时底层的 Node 数组(table)依然是 null,大小为 0。系统一行多余的代码都没跑,一点额外的内存都没占。

出生方式二:定制开局(带初始容量的构造)

当你预估到会有很多战利品,使用 new HashMap<>(20) 指定容量时:

  • 系统操作:除了设置负载因子,它还会释放一个名叫 tableSizeFor() 的魔法。
  • 核心机制:HashMap 的容量必须是 2 的 N 次幂。就算你传入的是 20,tableSizeFor() 也会通过精妙的位移运算,帮你自动纠正并计算出大于等于 20 的最小 2 的 N 次幂,也就是 32
  • 迷宫状态注意!此时数组依然没有被创建! 这个算出来的 32,会被暂时保存在一个叫 threshold(扩容阈值)的变量里“暂存”起来。

真正的迷宫何时降临?(懒加载的觉醒)

既然初始化的时候什么都没建,那什么时候才真正分配那 16 个(或者 32 个)房间呢?

答案是:当你第一次挥剑,执行 put() 动作,存放第一个战利品的时候。

当第一个元素准备进入 HashMap 时,系统会发现 table 是空的,这时候就会触发我们上一关提到的 “扩容法阵 (resize())” 。在这次特殊的初始扩容中,系统才会真正根据之前的设定,在内存中划分出对应的数组空间。

总结:你的通关笔记

  • 动作:初始化只是“配置参数”,并没有“分配内存”。
  • 目的:为了节省内存。如果你 new 了一个 HashMap 却一直没往里面放东西,不提前创建数组就不会浪费宝贵的内存空间。
  • 掉落知识点tableSizeFor() 魔法(无论你输入什么数字,都能强制转换为 2 的 N 次方)。

欲知后事,请看下回分解