php Hash Table(一) Hash Table的结构
关于Hash Table专题:
一直想深入理解一下php的hash table的实现,以前一直是星星点点的看看,从未彻底的总结过,那就从这个专题开始吧!
主要想总结几个部分:hashtable结构,hashtable实现,hashtable使用。
参考博客:
现代魔法学院 :http://www.nowamagic.net/academy/detail/1200001
Veda原型:http://www.nowamagic.net/librarys/veda/detail/1348
猫爷:http://songqi.sinaapp.com/blog/category/php-develop/
鸟哥:http://www.laruence.com/2009/08/23/1065.html
let's go!
Hash Table的结构图:

在上图中发现:Bucket1和Bucket2是hash冲突的双向链表,但是后添加的Bucket2是添加到头部的,可以看到Bucket2的pListLast和pNext指向Bucket1。(增加元素的时候, 元素会插在相同Hash元素链的头部和线性列表的尾部--鸟哥)
对HashTable结构体的字段解释:
1.nTableSize。整个哈希表分配的大小(在内部实现的C中分配的数组大小,PHP是动态的,但到底层数组是有大小的是静态的),他的大小有一个固定的申请算法,nTableSize = pow(ceil(log(nTableSize,2))),举个例子来看,如果PHP数组存储32个整形数据,那么底层申请的nTableSize应该等于32个元素,如果33呢,那么取最近且大于这个数的一个数64,那么分配的大小是64个元素。这样分配的原因是为了能分配足够的内存同样又不会浪费太多的内存。基于哈希的效率考虑,太小那么势必造成哈希之后太多的碰撞查找,如果分配太大那么必然浪费太多内存,这样分配经过实践证明相对在空间和时间上可以获得一个平衡。
2.nTableMask。哈希表的掩码数值等于nTableSize-1,他的作用是什么?用来纠正通过DBJ算法计算的哈希值在当前nTableSize大小的哈希表中的正确的索引值。比如"foo"通过固定算法之后得出的哈希值是193491849,如果表的大小为64,很明显已经超过了最大索引值,这时候就需要运用哈希表的掩码对其进行矫正实际采用的方法就是与掩码进行位与运算,这样做是为了把哈希值大的一样映射到nTalbeSize空间内。
hash | | 0b1011100010000111001110001001
& mask | & | & 0b0000000000000000000000111111
---------------------------------------------------------
= index | = | = 0b0000000000000000000000001001
3.nNumOfElements。是PHP数组中实际存储元素的个数,我们使用count,sizeof计算的就是获取的这个值。
4.nNextFreeElement。下一个空闲的元素空间,当我们申请一个空下标元素的时候就需要用到此项,比如$ret[] = 'apple'。
5.pInternalPointer。存储了内部当前执行的元素的指针,当我们使用一些内部循环函数的时候会用到这个指针比如reset(), current(), prev(), next(), foreach(), end()。
6.pListHead和pListTail则具体指向了该哈希表的第一个和最后一个元素,对应就是数组的起始和结束元素。
7.arBuckets。这个就是实际存储的C的内部数组。这里记录的是一个指向指针的指针Bucket **。即指向一个指针数组,其中每个元素是一个指向Bucket链表的头指针。
8.pDestructor 是一个析构函数,当某个值被从哈希表删除的时候会触发此函数。他还有一个主要作用是用于变量的GC回收。在PHP里面GC是通过引用计数实现的,当一个变量的引用计数变为0,就会被PHP的GC回收。
9.persistent 定义了hashtable是否能在多次request中获得持久存在。
10.nApplyCount 和 bApplyProtection 是用来防止无限递归的。关于nApplyCount的意义, 我们可以通过一个例子来了解:
<?php
$arr = array(1,2,3,4,5,);
$arr[] = &$arr; var_export($arr); //Fatal error: Nesting level too deep - recursive dependency?
这个字段就是为了防治循环引用导致的无限循环而设立的.
11.inconsistent 是在调试模式下捕获对HT不正确的使用。
在zend/Zend_hash.h中对hashtable的定义:
typedef struct _hashtable {
uint nTableSize; // hash Bucket的大小,最小为8,以2x增长。
uint nTableMask; // nTableSize-1 , 掩码,用于根据hash值计算存储位置。
uint nNumOfElements; // hash Bucket中当前存在的元素个数,count()函数会直接返回此值
ulong nNextFreeElement; // 下一个数字索引的位置,$arr[] = "hello"时会用到
Bucket *pInternalPointer; // 当前遍历的指针(foreach比for快的原因之一)
Bucket *pListHead; // 存储数组头元素指针
Bucket *pListTail; // 存储数组尾元素指针
Bucket **arBuckets; // 存储hash数组
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
对Bucket结构体字段的解释:
1.h是一个哈希值,未经过掩码矫正的哈希DBJ算出来的原始值。或是数字索引的数字(通过nKeyLength=0来表示是数字索引)。而对于字符串索引来说, 索引值保存在arKey中, 索引的长度保存在nKeyLength中.
2.arKey,用来记录作为哈希计算的字符串,nKeyLength是哈希字符串的长度,对于整形键值是用不到这两项的。
3.pData以及pDataPtr是实际存储数据的指针,在PHP里面他们通常是指向一个zval结构。在Bucket中,实际的数据是保存在pData指针指向的内存块中,通常这个内存块是系统另外分配的。但有一种情况例外,就是当Bucket保存 的数据是一个指针时,HashTable将不会另外请求系统分配空间来保存这个指针,而是直接将该指针保存到pDataPtr中,然后再将pData指向 本结构成员的地址。这样可以提高效率,减少内存碎片。由此我们可以看到PHP HashTable设计的精妙之处。如果Bucket中的数据不是一个指针,pDataPtr为NULL。
4.pListNext, pListLast 指定了整个数组的顺序,PHP中的遍历就是通过哈希结构体中的pListHead bucket依次遍历pListNext直到数组结束。
5.pNext和pLast 这两个指针是用来解决哈希冲突的,这个在下面哈希冲突中详细介绍,在PHP的哈希表冲突的处理采用的是拉链法也就是在每个可能冲突的键值位置拉出一个链表来存储对应的键值数据。
6.arKey 最后一个元素, 这个是flexible array技巧, 可以节省内存,和方便初始化的一种做法, 具体的参看http://blog.csdn.net/zhangboyj/article/details/6232168 (c99 柔性数组成员),博文中特意指出不能用arKey[1]的写法,这个我现在还不太懂。
在zend/Zend_hash.h中对bucket的定义:
typedef struct bucket {
/* Used for numeric indexing */
ulong h; // 对char *key进行hash后的值,数字索引的话就是索引值
uint nKeyLength; // hash关键字的长度,如果数组索引为数字,此值为0
void *pData; // 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr
void *pDataPtr; //如果是指针数据,此值会指向真正的value,同时上面pData会指向此值
struct bucket *pListNext; // 整个hash表的下一元素
struct bucket *pListLast; // 整个哈希表该元素的上一个元素
struct bucket *pNext; // 存放在同一个hash Bucket内的下一个元素
struct bucket *pLast; // 同一个哈希bucket的上一个元素
char arKey[];
/*存储字符索引,此项必须放在最未尾,因为此处只字义了1个字节,存储的实际上是指向char *key的值,
这就意味着可以省去再赋值一次的消耗,而且,有时此值并不需要,所以同时还节省了空间。
*/
} Bucket;
php Hash Table(一) Hash Table的结构的更多相关文章
- OpenFlow Switch学习笔记(五)——Group Table、Meter Table及Counters
本文主要详述OpenFlow Switch的另外两个主要组件——Group Table和Meter Table,它们在整个OpenFlow Swtich Processing中也起到了重要作用. 1. ...
- Hash表及hash算法的分析
Hash表中的一些原理/概念,及根据这些原理/概念: 一. Hash表概念 二. Hash构造函数的方法,及适用范围 三. Hash处理冲突方法,各自特征 四. ...
- Hash::make与Hash::check
调用方法之前要先去引用: use Illuminate\Support\Facades\Hash; 可以调用 Hash 门面上的 make 方法对存储密码进行哈希: $pwd = Hash::make ...
- MySQL删除大表时潜在的问题(drop table,truncate table)
来源于:https://www.cnblogs.com/CtripDBA/p/11465315.html,侵删,纯截图,避免吸引流量之嫌 case1,删除大表时,因为清理自适应hash索引占用的内容导 ...
- 查询计划Hash和查询Hash
查询计划hash和查询hash 在SQL Server 2008中引入的围绕执行计划和缓冲的新功能被称为查询计划hash和查询hash.这是使用针对查询或查询计划的算法来生成二进制hash值的二进制对 ...
- delete table 和 truncate table
delete table 和 truncate table 使用delete语句删除数据的一般语法格式: delete [from] {table_name.view_name} [where< ...
- 14.4.3 Adaptive Hash Index 自适应hash index
14.4.3 Adaptive Hash Index 自适应hash index 自适应hash index(AHI) 让InnoDB 执行更像内存数据库在系统使用合适的负载组合和足够的内存用于Buf ...
- 14.2.5.6 Adaptive Hash Indexes 自适应Hash Indexes
14.2.5.6 Adaptive Hash Indexes 自适应Hash Indexes adaptive hash index(AHI) 让InnoDB 执行更加像在一个内存数据库里在, 在不牺 ...
- 转载:字符串hash总结(hash是一门优雅的暴力!)
转载自:远航休息栈 字符串Hash总结 Hash是什么意思呢?某度翻译告诉我们: hash 英[hæʃ] 美[hæʃ]n. 剁碎的食物; #号; 蔬菜肉丁;vt. 把…弄乱; 切碎; 反复推敲; 搞糟 ...
随机推荐
- 02python算法-递推
递推 1什么是递推?:根据已有节点的值,以及规律推出之后节点的值 2为什么要用递推:简单的解决有规矩事件 3怎么用?: 我们举个经典的例子: 如果1对兔子每月能生1对小兔子,而每对小兔在它出生后的第3 ...
- xml解析工具-jdom
前言:近期接触SSH框架的时候,经常得配置一下xml文件:今天闲来没事就挖挖xml解析的原理供大伙儿分享.本文主要通过一个简单的例子解析一个xml文件.明白其中缘由之后,大家想定义自己的xml也绝非难 ...
- bzoj4400: tjoi2012 桥
先传代码再填坑 #include <iostream> #include <cstdio> #include <cmath> #include <cstrin ...
- [poj2446]Chessboard
Description 给定一个m×n的棋盘,上面有k个洞,求是否能在不重复覆盖且不覆盖到洞的情况下,用2×1的卡片完全覆盖棋盘. Input 第一行有三个整数n,m,k(0<m,n<=3 ...
- C++标准库简介、与STL的关系。
转自http://www.cnblogs.com/xiongjiaji/archive/2011/06/22/2476490.html C++标准库的所有头文件都没有扩展名.C++标准库的内容总共在5 ...
- 【codevs1170】 双栈排序
http://codevs.cn/problem/1170/ (题目链接) 题意 给出一个初始序列,判断能否通过两个栈的入栈和出栈操作构造出一个有序序列.若可以,输出字典序最小的方案. Solutio ...
- Visual Studio插件
不定时更新,得到最好用的插件.(友情提示:安装插件时最好先备份系统) 1.Resharper 10.0.0.12.VS10x CodeMAP3.JavaScript Map Parser4.JScri ...
- ecshop /includes/init.php Arbitrary User Login Vul
catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 对用户输入的cookie,判断免登的逻辑中存在漏洞,导致黑客可以直接通过 ...
- linux查看某个进程的线程id(spid)
鉴于linux下线程的广泛使用 我们怎么查看某个进程拥有的线程id了 现在很多服务的设计 主进程->子进程->线程(比如mysql,varnish) 主进程负责侦听网络上的连接 并把连接发 ...
- centos虚拟机克隆
vmware vsphere平台上克隆centos6 一.删掉/etc/udev/rules.d/70-persistent-net.rules文件 reboot 二.vim ifcfg-eth0 删 ...