PHP7数组底层原理
PHP底层是由HashTable+Bucket实现
HashTable
typedef struct HashTable{ uint nTableSize; //哈希表的大小 uint nTableMask; //哈希表掩码,用以矫正过长的哈希值 ulong nNumOfElements; //记录当前哈希表存储了多少个元素,用count($arr)其实就是取出hash表的这个数据 ulong NextFreeELement; //记录下一个空闲位置的索引位置,$arr[]=$value里的$value就会放到该空间。 Bucket* pListHead; //记录PHP数组的第一个元素 Bucket* pLstTail; //记录PHP数组的最后一个元素 Bucket* pInternalPointer; //记录当前哈希表指向的Bucket,在foreach,current,next,prev等等会用到, Bucket** arBuckets; //指向存储实际Hash数组的指针的指针。 }
Bucket
typedef struct Bucket{ ulong h;//哈希值 uint nKeyLength; //key的长度,如果key是整形,则此项不需要赋值 Bucket* pNext; //该桶后面的桶,冲突处理的桶 Bucket* pLast; //该桶前面的桶,冲突处理的桶 Bucket* pListNext; //用以记录数组的顺序,该元素前一个元素。 Bucket* pListLast; //用以记录数组的顺序,该元素后一个元素。 const char * pData; //模拟记录PHP数据,原来是void *pData和 void *pDataPtr char arKey[1] //记录key,之所以是[1]是因为这是柔性成员,具体可以百度C99柔性成员 }Bucket;
二、存储过程
通过hash函数对key做hash,算出在中间表的下标,数据顺序的存储在Bucket数组(链表)区(这也是为什么PHP数组能有序,实际就是顺序存储的),当遇到hash冲突时,采用拉链法解决hash冲突。元素删除也是先进行逻辑删除,当插入新元素发现Bucket满了时,检查已经删除的bucket是否达到比例,如果达到,则将已删除的 Bucket 移除,然后把后面的 Bucket 往前移动补上空位,如果还没有达到阈值则会分配一个原数组大小 2 倍的新数组,然后把原数组的元素复制到新数组上,最后重建索引,重建索引会将已删除的 Bucket 移除。
三、遍历
foreach遍历是基于hashtable的指针遍历,相当于直接遍历bucket数组(链表),遍历出来为插入元素的顺序而非键值顺序,而for遍历,要先算key的hash值,再找到实际的bucket,所以foreach要比for快。
参考:剖析PHP底层数组是如何实现的 - {-)大傻逼 - 博客园 (cnblogs.com)