標籤:直接 c 語言 image http des mda 空間 移除 height
本篇文章主要是對 PHP HashTable 總結,下面的參考連結是很好的學習資料。學習“散列”這個資料結構—推薦《資料結構與演算法分析 C語言描述》
總結
HashTable 又叫做散列表,是一種用於以常數平均時間執行插入、刪除和尋找的技術。不能有效支援元素之間的排序。——《資料結構與演算法分析 C語言描述》
HashTable 是 PHP 的靈魂,因為在 Zend 引擎中大量的使用了 HashTable,如變數表,常量表,函數表等,這些都是使用 HashTable 儲存的,另外,PHP 的數組也是通過使用 HashTble 實現的。
接下來看下面這一句話:
Hashtable是非常常見的資料結構,它被設計出來解決電腦只能直接表示以連續的整數作為索引的數組的問題。使用Hashtable,程式員才能使用字串或者其他的複合類型作為數組的鍵。
Hashtable 的概念:字串的鍵先會被傳遞給一個 hash 函數(hashing function,中文也翻譯為散列函數),然後這個函數會返回一個整數,而這個整數就是“通常”的數組的索引,通過這個數字索引可以訪問到 字串的鍵對應的資料。
關於 HashTable 的幾個概念
- 鍵(key):用於操作資料的標示,例如PHP數組中的索引,或者字串鍵等等。
- 槽(slot/bucket):雜湊表中用於儲存資料的一個單元,也就是資料真正存放的容器。
- 雜湊函數(hash function):將key映射(map)到資料應該存放的slot所在位置的函數。
- 雜湊衝突(hash collision):雜湊函數將兩個不同的key映射到同一個索引的情況。
對比 PHP 的數組和 C 語言的數組,發現 PHP 的數組確實支援更多的寫法,下標不僅可以是數字也可以是字母等。另一方面 HashTable 是無序的,那 PHP 數組的順序結構是怎麼實現的呢?可以帶著這些疑問閱讀 PHP 源碼。
源碼版本: php-7.1.19-src
解決雜湊衝突一般有兩種方式,PHP 使用的是分離連結法,既當產生衝突時,資料形成一個鏈表。
Hashtable 的資料結構
1 typedef struct _Bucket { 2 zval val; /* 儲存的具體資料 */ 3 zend_ulong h; /* 一個 hash 值 */ 4 zend_string *key; /* 一個字串鍵 */ 5 } Bucket; 6 7 typedef struct _zend_array HashTable; 8 9 struct _zend_array {10 zend_refcounted_h gc;11 union {12 struct {13 ZEND_ENDIAN_LOHI_4(14 zend_uchar flags,15 zend_uchar nApplyCount,16 zend_uchar nIteratorsCount,17 zend_uchar consistency)18 } v;19 uint32_t flags;20 } u;21 uint32_t nTableMask; /* 掩碼,用於根據hash值計算儲存位置,永遠等於nTableSize-1 */22 Bucket *arData; /* 存放實際資料 */23 uint32_t nNumUsed; /* arData數組已經使用的數量 */24 uint32_t nNumOfElements; /* hash表中元素個數 */25 uint32_t nTableSize; /* hash表的大小 */26 uint32_t nInternalPointer; /* 用於HashTable遍曆 */27 zend_long nNextFreeElement; /* 下一個空閑可用位置的數字索引 */28 dtor_func_t pDestructor; /* 解構函式 */29 };
結構體 _Bucket 代表的是 PHP 裡數組的元素, _zend_array 為 PHP 具體功能添加了一些必要的資料。
例如當將一個元素從雜湊表刪除時並不會將對應的Bucket移除,而是將Bucket儲存的zval標示為IS_UNDEF
,所以使用 nNumOfElements 儲存 Hash 的元素個數,使用 nNumUsed 表示數組真實的使用數量。
nTableSize 表示分配的記憶體大小,最小為8。
HashTable中另外一個非常重要的值 arData
,這個值指向儲存元素數組的第一個Bucket,插入元素時按順序依次插入數組,比如第一個元素在arData[0]、第二個在arData[1]...arData[nNumUsed]。PHP數組的有序性正是通過arData
保證的。
雜湊表實現的關鍵是有一個數組儲存雜湊值與 Bucket 的映射,但是HashTable中並沒有這樣一個索引數組。實際上這個索引數組包含在arData
中,在記憶體中一塊存在。具體的位置如。
所以,整體來看 HashTable 主要依賴 arData 實現元素的儲存、索引。插入一個元素時先將元素插入Bucket數組,位置是 index,再根據key的雜湊值與nTableMask計算出索引數組的位置,將 index 存入這個位置;尋找時先根據 key 的雜湊值與 nTableMask 計算出索引數組的位置,獲得元素在 Bucket 數組的位置 index,再從 Bucket 數組中取出元素。
雜湊表的大小為2^n,插入時如果容量不夠則首先檢查已刪除元素所佔比例,如果達到閾值(ht->nNumUsed - ht->nNumOfElements > (ht->nNumOfElements >> 5),則將已刪除元素移除,重建索引,如果未到閾值則進行擴容操作,擴大為當前大小的2倍,將當前Bucket數組複製到新的空間,然後重建索引。
參考
PHP 7中新的Hashtable實現和效能改進
PHP internals Book
PHP 雜湊表(數組)的核心實現
PHP HashTable介紹總結