如下爲 PHP 數組的基礎結構,插入,查找和 rehash 過程。數組
struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar consistency) } v; uint32_t flags; } u; uint32_t nTableMask; // 哈希值計算掩碼,等於nTableSize的負值(nTableMask = -nTableSize) Bucket *arData; // 存儲元素數組,指向第一個Bucket uint32_t nNumUsed; // 已用Bucket數 uint32_t nNumOfElements; // 哈希表有效元素數 = nNumUsed - num(is_undef) uint32_t nTableSize; // 哈希表總大小,爲2的n次方, 最小爲8 uint32_t nInternalPointer; // 懷疑是內部指針 zend_long nNextFreeElement; // 下一個可用的數值索引 arr[] = 1;arr["a"] = 2;arr[] = 3; 則nNextFreeElement = 2; dtor_func_t pDestructor; }; typedef struct _Bucket { zval val; // 存儲的具體value zend_ulong h; // hash value (or numeric index) zend_string *key; // string key or NULL for numerics } Bucket;
value
,再保存 value
的位置。value
,而 value
按順序保存,其存儲位置會保存在由 key
計算 hash
取模 nTableMask
獲得的 idx
中。idx
區會所有初始化爲 -1,rehash
的時候也會初始化爲 -1。type
標記爲 is_undef
, 而且 nNumOfEmelment - 1
,若是該元素爲最後一個元素,那麼 nNumUsed - 1
。以 $arr = ['a'=>1, 'b'=>2] 爲例:ui
val.u2.next = -1
, 根據其下標 a
計算 hash
, 而後 hash
取模 nTableMask
獲得一個 idx
, 在該 idx
的位置保存前邊保存 1
的索引 nindex
。val.u2.next = -1
, 若是根據其下標 b
計算hash
取模 nTableMask
獲得的 idx
中已經有值,那麼說明出現了哈希碰撞,這個時候把當前 idx
中的值取出來保存到當前 val.u2.next
,把保存 2 的索引 nindex
保存在當前 idx
,以此類推。根據下標 a
計算 hash
取模 nTableMask
獲得一個 idx
,拿到該 idx
中的值 nindex
去 arData
中查找,若是找到的位置中的 key != a
, 那麼找不到;若是找到的位置中的 key == a
,那麼檢查其 u2.next
, 若是爲 -1, 那麼找到了;若是不爲-1,說明插入的過程當中出現了哈希衝突,那麼根據 u2.next
繼續在 arData
中查找,直到找到爲止。指針
rehash
的時候,首先把 nindex
區的全部記錄所有重置爲 -1,而後從第一個元素開始挪動指針 *p
,若是元素沒有被標記爲 is_undef
,那麼從新計算該元素的 key hash
並放到 nindex
,而後循環, p++
。若是元素被標記爲 is_undef
, 那麼繼續挪動指針 p++
,並設置一個新的指針 j
指向該位置,繼續循環,把後邊不爲 is_undef
的元素一個一個挪到前邊來,p
每次移動,j
遇到 is_undef
就不移動,直到被賦值。一直挪動到最後的 nNunUsed
,那麼把 j
賦值給 nNunUsed
,以後再插入元素的時候就從這個位置開始插入,之前的元素直接被覆蓋就是了。code