PHP 源代码分析 Zend HashTable详解第1/3页

2015-01-24信息快讯网

在PHP的Zend引擎中,有一个数据结构非常重要,它无处不在,是PHP数据存储的核心,各种常量、变量、函数、类、对象等都用它来组织,这个数据结构就是HashTable。

HashTable在通常的数据结构教材中也称作散列表,哈希表。其基本原理比较简单(如果你对其不熟悉,请查阅随便一本数据结构教材或在网上搜索),但PHP的实现有其独特的地方。理解了HashTable的数据存储结构,对我们分析PHP的源代码,特别是Zend Engine中的虚拟机的实现时,有很重要的帮助。它可以帮助我们在大脑中模拟一个完整的虚拟机的形象。它也是PHP中其它一些数据结构如数组实现的基础。
Zend HashTable的实现结合了双向链表和向量(数组)两种数据结构的优点,为PHP提供了非常高效的数据存储和查询机制。
Let's begin!
一、 HashTable的数据结构
在Zend Engine中的HashTable的实现代码主要包括zend_hash.h, zend_hash.c这两个文件中。Zend HashTable包括两个主要的数据结构,其一是Bucket(桶)结构,另一个是HashTable结构。Bucket结构是用于保存数据的容器,而HashTable结构则提供了对所有这些Bucket(或桶列)进行管理的机制。
typedef struct bucket { 
ulong h; /* Used for numeric indexing */ 
uint nKeyLength; /* key 长度 */ 
void *pData; /* 指向Bucket中保存的数据的指针 */ 
void *pDataPtr; /* 指针数据 */ 
struct bucket *pListNext; /* 指向HashTable桶列中下一个元素 */ 
struct bucket *pListLast; /* 指向HashTable桶列中前一个元素 */ 
struct bucket *pNext; /* 指向具有同一个hash值的桶列的后一个元素 */ 
struct bucket *pLast; /* 指向具有同一个hash值的桶列的前一个元素 */ 
char arKey[1]; /* 必须是最后一个成员,key名称*/ 
} Bucket; 

在Zend HashTable中,每个数据元素(Bucket)有一个键名(key),它在整个HashTable中是唯一的,不能重复。根据键名可以唯一确定HashTable中的数据元素。键名有两种表示方式。第一种方式使用字符串arKey作为键名,该字符串的长度为nKeyLength。注意到在上面的数据结构中arKey虽然只是一个长度为1的字符数组,但它并不意味着key只能是一个字符。实际上Bucket是一个可变长的结构体,由于arKey是Bucket的最后一个成员变量,通过arKey与nKeyLength结合可确定一个长度为nKeyLength的key。这是C语言编程中的一个比较常用的技巧。另一种键名的表示方式是索引方式,这时nKeyLength总是0,长整型字段h就表示该数据元素的键名。简单的来说,即如果nKeyLength=0,则键名为h;否则键名为arKey, 键名的长度为nKeyLength。
当nKeyLength > 0时,并不表示这时的h值就没有意义。事实上,此时它保存的是arKey对应的hash值。不管hash函数怎么设计,冲突都是不可避免的,也就是说不同的arKey可能有相同的hash值。具有相同hash值的Bucket保存在HashTable的arBuckets数组(参考下面的解释)的同一个索引对应的桶列中。这个桶列是一个双向链表,其前向元素,后向元素分别用pLast, pNext来表示。新插入的Bucket放在该桶列的最前面。
在Bucket中,实际的数据是保存在pData指针指向的内存块中,通常这个内存块是系统另外分配的。但有一种情况例外,就是当Bucket保存的数据是一个指针时,HashTable将不会另外请求系统分配空间来保存这个指针,而是直接将该指针保存到pDataPtr中,然后再将pData指向本结构成员的地址。这样可以提高效率,减少内存碎片。由此我们可以看到PHP HashTable设计的精妙之处。如果Bucket中的数据不是一个指针,pDataPtr为NULL。
HashTable中所有的Bucket通过pListNext, pListLast构成了一个双向链表。最新插入的Bucket放在这个双向链表的最后。
注意在一般情况下,Bucket并不能提供它所存储的数据大小的信息。所以在PHP的实现中,Bucket中保存的数据必须具有管理自身大小的能力。
typedef struct _hashtable { 
uint nTableSize; 
uint nTableMask; 
uint nNumOfElements; 
ulong nNextFreeElement; 
Bucket *pInternalPointer; 
Bucket *pListHead; 
Bucket *pListTail; 
Bucket **arBuckets; 
dtor_func_t pDestructor; 
zend_bool persistent; 
unsigned char nApplyCount; 
zend_bool bApplyProtection; 
#if ZEND_DEBUG 
int inconsistent; 
#endif 
} HashTable; 


php 数组排序 array_multisort与uasort的区别
用php的ob_start来生成静态页面的方法分析
通过JavaScript或PHP检测Android设备的代码
PHP中uploaded_files函数使用方法详解
PHP $_FILES函数详解
理解php Hash函数,增强密码安全
PHP下通过file_get_contents的代理使用方法
.htaccess文件保护实例讲解
javascript,php获取函数参数对象的代码
仿AS3实现PHP 事件机制实现代码
php simplexmlElement操作xml的命名空间实现代码
PHP中simplexml_load_string函数使用说明
纯php打造的tab选项卡效果代码(不用js)
php iconv() : Detected an illegal character in input string
smarty中先strip_tags过滤html标签后truncate截取文章运用
PHP类中Static方法效率测试代码
DedeCMS dede_channeltype表字段注释
php htmlspecialchars加强版
PHP has encountered an Access Violation 错误的解决方法
PHP 执行系统外部命令 system() exec() passthru()
php natsort内核函数浅析第1/2页
PHP 生成的XML以FLASH获取为乱码终极解决
用PHP的ob_start() 控制您的浏览器cache
PHP file_get_contents 函数超时的几种解决方法
PHP 配置文件中open_basedir选项作用
php strlen mb_strlen计算中英文混排字符串长度
php addslashes 函数详细分析说明
C# Assembly类访问程序集信息
PHP加速 eAccelerator配置和使用指南
php 无限级 SelectTree 类
mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别
一个php导出oracle库的php代码
AspNetAjaxPager,Asp.Net通用无刷新Ajax分页控件,支持多样式多数据绑定
PHP syntax error, unexpected $end 错误的一种原因及解决
wiki-shan写的php在线加密的解密程序
php htmlentities和htmlspecialchars 的区别
How do I change MySQL timezone?
asp和php下textarea提交大量数据发生丢失的解决方法
Http 1.1 Etag 与 Last-Modified提高php效率
©2014-2024 dbsqp.com