PHP数组底层基于有序哈希表实现,兼顾索引与关联访问;通过双向链表保持插入顺序,packed array优化连续整数键访问,zval引用计数支持写时复制,PHP 7精简结构提升性能。

PHP 中的数组是一种复合数据类型,其底层实现既支持索引访问又支持关联键值映射,这种灵活性源于其内部结构设计。以下是 PHP 数组实现原理的关键解析:
PHP 5.x 及更早版本中,数组底层完全基于哈希表实现,每个数组对应一个 zend_array 结构体(PHP 7+ 中为 zend_array),其中包含桶(bucket)数组、哈希值索引表、元素数量及容量等字段。哈希表通过键的哈希值快速定位存储位置,实现平均 O(1) 的查找效率。
1、每个 bucket 存储一个 zval(PHP 变量容器)及键信息(包括字符串键或整数键标识)。
2、当发生哈希冲突时,PHP 使用开放寻址法中的线性探测方式向后查找空闲 bucket。
立即学习“PHP免费学习笔记(深入)”;
3、哈希表在元素数量超过容量阈值时触发扩容,新容量为原容量的两倍,并重新计算所有键的哈希位置。
PHP 数组保持插入顺序,是因为每个 bucket 中额外维护了 prev 和 next 指针,构成双向链表。该链表独立于哈希索引,仅用于遍历和 foreach 操作,确保键值对按写入顺序返回。
1、每次插入新元素时,新 bucket 被追加到链表尾部。
2、删除元素时,仅断开链表指针并标记 bucket 为“已删除”,不立即回收内存空间。
3、遍历时跳过被标记为“已删除”的 bucket,直到链表结束。
当数组以连续整数为键(如 0,1,2,…)且无空洞时,PHP 7 引入了 packed array 优化路径:此时哈希表的 hash 字段可为空,直接通过下标偏移访问 data 数组中的 zval,避免哈希计算与指针跳转,显著提升数值索引访问性能。
1、判断是否为 packed array 的条件包括:所有键为非负整数、从 0 开始、无缺失、数量等于 size。
2、packed array 下的 key 查找退化为数组下标运算,时间复杂度为 O(1)。
3、一旦插入非整数键或出现空洞(如 unset($arr[1])),该数组即退化为普通哈希表模式。
数组中每个元素实际存储的是一个 zval 结构,其中包含类型、值、refcount(引用计数)和 u1.gc(垃圾收集信息)。PHP 通过引用计数实现写时复制(Copy-on-Write),多个变量指向同一数组时共享底层 HashTable,仅在某一方尝试修改时才分离副本。
1、初始化数组时,zval 的 refcount 设为 1。
2、将数组赋值给另一变量($b = $a)时,refcount 增至 2,但不复制 HashTable 数据。
3、当 $a[] = 'new' 执行时,检测 refcount > 1,则分配新 HashTable 并复制内容,再执行插入。
PHP 7 将原来的 zend_hash_table 结构重构成更紧凑的 zend_array,移除了冗余字段,将 bucket 内联到数组主结构中,减少内存碎片与缓存未命中。同时引入固定大小的 bucket 结构(含 key、h、val、next),并用 uint32_t 替代指针存储索引,降低内存占用约 50%。
1、bucket 不再动态分配,而是随 zend_array 一次性分配连续内存块。
2、哈希索引表(arHash)改为 uint32_t 类型数组,每个槽位存储 bucket 在 data 中的索引而非指针。
3、删除操作不再移动后续 bucket,仅更新链表指针与哈希索引表中的对应项。
以上就是php面试题数组是怎么实现的_PHP面试题数组实现原理解析的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号