PHP空数组底层是已初始化的有序哈希表,含arData桶数组、nTableMask掩码、双向链表指针等完整结构,支持整数/字符串键统一处理、插入顺序保障及动态扩容。

PHP定义空数组看似简单,实则背后是一整套兼顾效率与灵活性的设计。它不是C语言那种固定长度、连续内存的“真数组”,而是一个有序哈希表(ordered hash table)——这个底层结构直接决定了PHP数组为什么能同时支持数字索引、字符串键名、混合插入、动态扩容和严格保持顺序。
空数组一创建,就已是完整哈希表
执行 $arr = []; 或 $arr = array(); 时,PHP并非只分配一个“空容器”,而是立即初始化一个最小规格的 zend_array 结构。它包含:
- 一块小容量的桶(
arData)内存,用于后续存放元素 - 哈希掩码(
nTableMask),默认为 -1(对应容量 1) - 元素计数器(
nNumOfElements)置为 0 - 双向链表头尾指针,已就绪,只为记录插入顺序
这意味着空数组从诞生起就具备全部能力:可 isset()、可 foreach、可 array_key_exists(),甚至可直接用 $arr['x'] = 1 赋值——无需任何额外初始化步骤。
键的处理统一而智能
无论你用整数还是字符串作键,PHP都通过同一套机制处理:
立即学习“PHP免费学习笔记(深入)”;
- 整数键(如
0、123)直接作为哈希值h字段,跳过计算 - 字符串键(如
'name')经 DJBX33A 算法生成 64 位哈希值,再与掩码位运算定位槽位 - 键类型自动归一化:
true→1,'2'→2,3.7→3
这种统一抽象让开发者无需区分“索引数组”和“关联数组”——它们在底层是同一种结构,只是键的表现形式不同。
插入顺序靠双向链表硬保障
普通哈希表不保证遍历顺序,但PHP数组必须。它的解决方案是在每个 Bucket 中额外维护 prev 和 next 指针,构成一条插入顺序链表。
所以即使两个键哈希冲突、落在同一槽位,它们在遍历时仍按实际插入先后出现。这也是为什么:
-
foreach ($arr as $k => $v)总是按写入顺序输出 -
array_keys()、array_values()返回结果严格对应原始顺序 - 多维配置数组(如路由规则、验证规则)结构清晰、语义明确
动态扩容不打断逻辑,也不牺牲性能
空数组初始容量极小(通常 1 或 8 个桶),但一旦元素增多、哈希冲突上升,PHP会自动触发扩容:
- 新容量总是 2 的幂次(如 1→2→4→8→16…),确保哈希索引可用位运算
hash & nTableMask快速完成 - 扩容时重建整个哈希表,所有 Bucket 重散列,但双向链表顺序不变
- 删除操作不立即释放内存,仅标记为“已删除”,等下次插入或
rehash时批量清理
这一设计让数组既能从小起步节省内存,又能随业务增长平滑扩展,完全对开发者透明。



















