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)

 

posted @ 2022-03-03 23:31  hugeQAQ  阅读(147)  评论(0编辑  收藏  举报