Hashtable 的概念:字符串的键先会被传递给一个 hash 函数(hashing function,中文也翻译为散列函数),然后这个函数会返回一个整数,而这个整数就是“通常”的数组的索引,通过这个数字索引可以访问到 字符串的键对应的数据。
本质上PHP的数组是有序的字典,表示k-v对的是有序列表,其中k-v映射是使用hashTable实现的,PHP将字符串key通过哈希函数运算返回一个整数,这个整数倍用作普通数组的索引,但是可能存在hash冲突,其中HashTable需要实现某种机制来解决冲突
关于 HashTable 的几个概念
- 键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。
- 槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。
- 哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。
- 哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。
PHP使用了双向链表和Hash表结合的方式实现HashTable,这使得HashTable能够在O(1)的时间复杂度上实现任意key的查询,同时又可以支持HashTable的遍历。
接下来看看HashTable的定义,大致做了下注释,和鸟哥的注释是一样的:
//zend_types.h
typedef struct _Bucket {
zval val;
zend_ulong h; /* hash value (or numeric index) 哈希算法算出 */ 来的hash值
zend_string *key; /* string key or NULL for numerics */
} Bucket;
typedef struct _zend_array HashTable;
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar reserve)
} v;
uint32_t flags;
} u;
uint32_t nTableMask; //哈希值计算掩码,等于nTableSize的负值(nTableMask = ~nTableSize + 1),计算最终落在那个Bucket的值
Bucket *arData; //存储元素数组,指向第一个Bucket,数组中每个元素都是Bucket
uint32_t nNumUsed; //arData数组已经使用的数量,已用Bucket数
uint32_t nNumOfElements; //哈希表已有元素数
uint32_t nTableSize; //哈希表总大小,为2的n次方
uint32_t nInternalPointer;//内部的指针,用于HashTable遍历
zend_long nNextFreeElement; //下一个可用的数值索引,如:arr[] = 1;arr["a"] = 2;arr[] = 3; 则nNextFreeElement =