php 数组的实现
PHP 数组的实现原理
PHP 的数组是一种混合数据结构,既支持数字索引也支持字符串键名。其底层实现基于哈希表(HashTable)和双向链表的组合,提供了高效的查找和遍历能力。
哈希表结构
PHP 数组的核心是哈希表(HashTable),它使用哈希函数将键名映射到存储位置。哈希表由多个桶(Bucket)组成,每个桶存储一个键值对。
哈希冲突通过链表法解决,当多个键名映射到同一个桶时,它们会以链表形式连接。PHP 7 优化了哈希表的内存布局,减少了内存占用。
双向链表维护顺序
PHP 数组是有序的,插入顺序会被保留。这是通过双向链表实现的,每个 Bucket 包含 pListLast 和 pListNext 指针,分别指向前驱和后继元素。

遍历数组时,PHP 会按照双向链表的顺序访问元素,而非哈希表中的物理存储顺序。这使得 foreach 能按插入顺序输出元素。
内存管理
PHP 数组使用引用计数和写时复制(Copy-On-Write)机制管理内存。当数组被复制时,仅增加引用计数而不立即分配新内存,直到有修改操作发生。

删除元素时,内存不会立即释放,而是标记为无效,等待后续重用或垃圾回收。这种策略减少了频繁内存分配的开销。
动态扩容
当哈希表负载因子超过阈值时,PHP 会自动扩容。新容量通常为当前大小的 2 倍左右,所有元素会重新哈希到新的桶中。扩容操作保证哈希表保持高效查找性能。
性能特征
- 查找操作平均时间复杂度为 O(1),最坏情况 O(n)(哈希冲突严重时)
- 插入和删除操作平均 O(1),最坏 O(n)
- 遍历操作 O(n),按插入顺序进行
- 内存占用较高,每个元素需要额外存储哈希值和链表指针
优化建议
- 预分配数组大小可避免频繁扩容,如
$arr = new SplFixedArray(100) - 大量数据时考虑使用
SplFixedArray减少内存占用 - 避免混合使用数字和字符串键名,可能影响哈希效率
- 超大数组可尝试分片存储或使用专门的数据结构
底层代码示例
以下是简化版哈希表结构的伪代码表示:
typedef struct _Bucket {
zval val; // 存储的值
zend_ulong h; // 哈希值
zend_string *key; // 字符串键名
struct _Bucket *next; // 哈希冲突链表
struct _Bucket *pListLast;// 双向链表前驱
struct _Bucket *pListNext;// 双向链表后继
} Bucket;
typedef struct _HashTable {
uint32_t nTableSize; // 哈希表大小
uint32_t nNumUsed; // 已用桶数
uint32_t nNumOfElements;// 有效元素数
Bucket *arData; // 桶数组
uint32_t *arHash; // 哈希索引
} HashTable;






