PHP 數組

如下爲 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 中。
  • 數組初始化的時候最小大小爲 8,以此爲16,32,64。。。
  • 數組初始化的時候邊作的 idx 區會所有初始化爲 -1,rehash 的時候也會初始化爲 -1。
  • 數組中刪除一個元素的時候,是把該刪除的元素的 type 標記爲 is_undef, 而且 nNumOfEmelment - 1,若是該元素爲最後一個元素,那麼 nNumUsed - 1

插入:

以 $arr = ['a'=>1, 'b'=>2] 爲例:ui

  1. 首先把 1 放到數組中,其 val.u2.next = -1, 根據其下標 a 計算 hash, 而後 hash 取模 nTableMask 獲得一個 idx, 在該 idx 的位置保存前邊保存 1 的索引 nindex
  2. 再存放 2, 其 val.u2.next = -1, 若是根據其下標 b 計算hash 取模 nTableMask 獲得的 idx 中已經有值,那麼說明出現了哈希碰撞,這個時候把當前 idx 中的值取出來保存到當前 val.u2.next,把保存 2 的索引 nindex 保存在當前 idx,以此類推。

查找:

根據下標 a 計算 hash 取模 nTableMask 獲得一個 idx ,拿到該 idx 中的值 nindexarData 中查找,若是找到的位置中的 key != a, 那麼找不到;若是找到的位置中的 key == a,那麼檢查其 u2.next, 若是爲 -1, 那麼找到了;若是不爲-1,說明插入的過程當中出現了哈希衝突,那麼根據 u2.next 繼續在 arData 中查找,直到找到爲止。指針

rehash:

rehash 的時候,首先把 nindex 區的全部記錄所有重置爲 -1,而後從第一個元素開始挪動指針 *p,若是元素沒有被標記爲 is_undef,那麼從新計算該元素的 key hash 並放到 nindex,而後循環, p++。若是元素被標記爲 is_undef, 那麼繼續挪動指針 p++,並設置一個新的指針 j 指向該位置,繼續循環,把後邊不爲 is_undef 的元素一個一個挪到前邊來,p 每次移動,j 遇到 is_undef 就不移動,直到被賦值。一直挪動到最後的 nNunUsed ,那麼把 j 賦值給 nNunUsed,以後再插入元素的時候就從這個位置開始插入,之前的元素直接被覆蓋就是了。code

相關文章
相關標籤/搜索