这是一篇关于PHP中大量使用的数据结构HashTable的介绍,通过了解HashTable的存储原理,可以搞清楚一些问题,如数组的存储及一些操作原理、count()函数是如何得出数组元素个数的、foreach比for快的原因等等。HashTable是PHP的灵魂,因为在Zend引擎中大量地使用了HashTable,如变量表,常量表,函数表等,这些都是使用HashTable保存的,另外,PHP的数组也是通过使用HashTble实现的。
首先对Hashtable做个简单介绍,Hashtable的概念实际上非常简单:字符串的键首先会被传递给一个hash函数,然后这个函数会返回一个整数(我们把它叫做hash值),而这个整数就是“通常”的数组的索引。问题是对于两个不同的字符串,调用hash函数会得到同一个hash值,而现实情况是任意字符串都可以作为键,所以键会有无数个,而数组的大小必须是提前设定好的,因为hash值必须小于数组索引的最大值,所以可以生成的hash值必须是有限的。这样用有限的hash值表示无限的键,必然会导致冲突。我们把两个不同的键的hash值是一样的情况称为冲突,任何Hashtable算法都必须提供某种机制解决这种冲突。有两种主要的处理冲突的方法。开放定址法,当冲突发生的时候,冲突的元素会被保存到一个不同的索引中;链接法,所有拥有相同的hash值的元素,它们都会被保存到一个链表中。PHP使用的就是第二种方法。
PHP中的HashTable的实现代码保存在Zend/zend_hash.h和Zend/zend_hash.c这两个文件中,PHP使用如下两个数据结构来实现哈希表,HashTable结构体用于保存整个哈希表需要的基本信息:
typedef struct _hashtable {
uint nTableSize; // hash Bucket的大小,最小为8,以2x增长。
uint nTableMask; // nTableSize-1 , 索引取值的优化
uint nNumOfElements; // hash Bucket中当前存在的元素个数,count()函数会直接返回此值
ulong nNextFreeElement; // 下一个数字索引的位置
Bucket *pInternalPointer; // 当前遍历的指针(foreach比for快的原因之一)
Bucket *pListHead; // 存储数组头元素指针
Bucket *pListTail; // 存储数组尾元素指针
Bucket **arBuckets; // 存储hash数组
dtor_func_t p