php数组的内部实现
PHP 数组的内部实现
PHP 的数组是一种混合数据结构,既实现了有序映射(类似字典),又保留了插入顺序。其内部实现经过多次优化,以下是核心机制:
哈希表(HashTable)
PHP 数组底层使用哈希表存储数据,通过散列函数将键名映射到特定槽位。哈希表由两部分组成:

- 存储桶(Bucket):包含键、值、哈希值及链表指针的结构体
- 双向链表:维护元素的插入顺序,支持迭代时按插入顺序遍历
// PHP 内核中的 Bucket 结构(简化)
typedef struct _Bucket {
zval val; // 存储的值
zend_ulong h; // 哈希值(数值键直接使用)
zend_string *key; // 字符串键
struct _Bucket *next; // 哈希冲突链表
struct _Bucket *prev; // 双向链表前驱
} Bucket;
动态扩容机制
哈希表会根据元素数量动态调整容量:

- 初始默认大小为 8 个槽位
- 当元素数量超过当前容量的 3/4 时触发扩容
- 新容量为当前容量的 2 倍(直到达到预定义的最大值)
冲突解决
采用链地址法处理哈希冲突:
- 相同哈希值的元素通过链表连接
- PHP 7 优化为将冲突链表嵌入到存储桶数组,减少内存访问次数
有序性保证
通过维护双向链表实现有序遍历:
- 每个 Bucket 包含
prev和next指针 HT结构保存链表头尾指针,确保foreach按插入顺序输出
内存优化(PHP 7+)
- 打包数组(Packed Array):对连续数字键(0,1,2...)的数组,省略键名存储直接使用索引
- 内存局部性优化:将 Bucket 与值一起分配,减少 CPU 缓存未命中
性能特性
- 平均时间复杂度:
- 插入/删除:O(1)
- 查找:O(1)
- 内存占用:每个元素约 56 字节(PHP 7+ 优化前为 96 字节)
特殊操作的影响
unset()不会立即释放内存,只是标记为IS_UNDEFarray_splice()会导致重建双向链表,时间复杂度 O(n)- 数值键和字符串键的哈希方式不同:
$arr = [1 => 'a', "1" => 'b']; // 输出: [1 => "b"](二者哈希值相同)
代码示例观察行为
$array = [];
$array["foo"] = "bar"; // 哈希表插入字符串键
$array[42] = "answer"; // 数值键直接使用整数哈希
unset($array["foo"]); // 标记 Bucket 为 UNDEF
// 查看内部结构(需安装调试扩展)
debug_zval_dump($array);
通过理解这些机制,可以更高效地使用 PHP 数组,避免如频繁扩容或混合键类型导致的性能下降问题。






