欢迎来到 “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 次方)。
欲知后事,请看下回分解