本文转载自:  http://segmentfault.com/blog/tree/1190000000718519

HashTable对PHP来说是一种非常重要的数据结构。很多PHP的内部实现(变量的作用域,函数表,类的属性、方法,数组)就是通过HashTable来实现的。最近了解了一下PHP底层HashTable的实现。

PHP底层HashTable的实现有两个非常重要的结构分别是:HashTable和Bucket。

先说一下HashTable结构:

HashTable的底层实现代码如下:

typedef struct _hashtable{

uint nTableSize;         // hash Bucket的大小,最小为8

uint nTableMask;         //nTableSize - 1, 索引取值的优化

uint nNumofElements      // bucket 里面存的总数

ulong nNextFreeElement   //下一个数字索引的位置

Bucket *pInternalPointer  //当前遍历的指针(foreach比较快的原因)

Bucket *pListHead         //整个hashtable的头指针

Bucket *pListTail         //整个hashTable的尾指针

Bucket **argBuckets       // Buceket 数组,用来存储数据

doctor_func_t pDestructor //删除元素时的回调函数,用于资源的释放

zend_bool persistent      //Bucket的内存分配方式,true使用系统的分配函数,false 使用php的内存分配函数

unsigned char nApplyCount //标记当前hash bucket 被递归的次数

zend_bool bApplyProtection

#if ZEND_DEBUG

int inconsistent

#endif

}HashTable

建议不太了解hash数据结构的同学先简单了解一下hash结构。

简单说一下php中hashtable的初始化操作:

代码如下:

ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)

{

uint i = 3;

//...

if (nSize >= 0x80000000) {

/* prevent overflow */

ht->nTableSize = 0x80000000;

} else {

while ((1U << i) < nSize) {

i++;

}

ht->nTableSize = 1 << i;

}

// ...

ht->nTableMask = ht->nTableSize - 1;

/* Uses ecalloc() so that Bucket* == NULL */

if (persistent) {

tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *));

if (!tmp) {

return FAILURE;

}

ht->arBuckets = tmp;

} else {

tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *));

if (tmp) {

ht->arBuckets = tmp;

}

}

return SUCCESS;

}

最开始判断需要初始化的hashtable大小是不是超过了系统能使用的最大大小。下面是对tablesize大小的一个处理。将用户自定义的大小改成需要的大小。例如:如果用户定义的hashtable大小是6,那初始化时,就会将6变成8,如果用户定义的大小为11,那初始化后的Hashtable的大小为16.

下面就是一个简单的判断,来决定是按照C语言本身的分配内存函数来分配内存,还是根据php封装好的内存分配函数来分配内存。

再谈一下 bucket的结构

typedef struct bucket{

ulong h;       //对key索引以后的值,数字key不做kash

uint nKeyLength; //key的长度

void *pData;

void *pDataPtr;   //指针数据,指向真实数据

struct bucket * pListNext; //整个hash表的下个元素

struct bucket *pListLast;   //整个hash表的上个元素

struct bucket *pNext;       //本bucket里面,下一个元素

struct bucket *pLast;       //本bucket里面的上一个元素

char arKey[1];

}Bucket

这里用一张网络上的很火的图来说明(图原地址没找到,没有做来源说明):

下面是引用了tipi里面的插入说明:

引用地址:tipi

如图中左下角的假设,假设依次插入了Bucket1,Bucket2,Bucket3三个元素:

1、插入Bucket1时,哈希表为空,经过哈希后定位到索引为1的槽位。此时的1槽位只有一个元素Bucket1。 其中Bucket1的pData或者pDataPtr指向的是Bucket1所存储的数据。此时由于没有链接关系。pNext, pLast,pListNext,pListLast指针均为空。同时在HashTable结构体中也保存了整个哈希表的第一个元素指针, 和最后一个元素指针,此时HashTable的pListHead和pListTail指针均指向Bucket1。

2、插入Bucket2时,由于Bucket2的key和Bucket1的key出现冲突,此时将Bucket2放在双链表的前面。 由于Bucket2后插入并置于链表的前端,此时Bucket2.pNext指向Bucket1,由于Bucket2后插入。 Bucket1.pListNext指向Bucket2,这时Bucket2就是哈希表的最后一个元素,这是HashTable.pListTail指向Bucket2。\3、插入Bucket3,该key没有哈希到槽位1,这时Bucket2.pListNext指向Bucket3,因为Bucket3后插入。 同时HashTable.pListTail改为指向Bucket3。

简单来说就是哈希表的Bucket结构维护了哈希表中插入元素的先后顺序,哈希表结构维护了整个哈希表的头和尾。 在操作哈希表的过程中始终保持预算之间的关系。

php底层HashTable的实现的更多相关文章

  1. PHP内核探索:哈希碰撞攻击是什么?

    最近哈希表碰撞攻击(Hashtable collisions as DOS attack)的话题不断被提起,各种语言纷纷中招.本文结合PHP内核源码,聊一聊这种攻击的原理及实现. 哈希表碰撞攻击的基本 ...

  2. 【转载】网络攻击技术(三)——Denial Of Service & 哈希相关 & PHP语言 & Java语言

    找到了这个系列的原始作者: http://www.cnblogs.com/rush/archive/2012/02/05/2339037.html 最近网络安全成了一个焦点,除了国内明文密码的安全事件 ...

  3. 第 13 章 StringTable详解

    目录 第 13 章 StringTable 1.String 的基本特性 1.1.String 概述 1.2.String 的基本特征 1.3.String 的底层结构 2.String 的内存分配 ...

  4. HashMap和ConcurrentHashMap和HashTable的底层原理与剖析

    HashMap  可以允许key为null,value为null,但HashMap的是线程不安全的  HashMap 底层是数组 + 链表的数据结构 在jdk 1.7 中 map集合中的每一项都是一个 ...

  5. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)

    HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...

  6. HashTable原理和底层实现

    1. 概述 上次讨论了HashMap的结构,原理和实现,本文来对Map家族的另外一个常用集合HashTable进行介绍.HashTable和HashMap两种集合非常相似,经常被各种面试官问到两者的区 ...

  7. Java 总结 数据底层原理 【包括 ArrayList、LinkedList、hash table、HashMap、Hashtable、ConcurrentHashMap、hash code、HashSet、LinkedHashMap、LinkedHashSet】

    1.ArrayList (1)底层是由动态数组实现的[使用了List接口]. (2)动态数组是长度不固定,随着数据的增多而变长. (3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会 ...

  8. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

  9. (转)HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

随机推荐

  1. Android 线性布局(LinearLayout)相关官方文档 - 指南部分

    Android 线性布局(LinearLayout)相关官方文档 - 指南部分 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用 ...

  2. 将Memcached作为服务自动启动

    1.最简单的做法 通常:启动Memcache的服务器端的命令为: /usr/local/bin/memcached -d -m 256 -u root -l 127.0.0.1 -p 12000 -c ...

  3. PHP-线程安全与非线程安全版本的区别

    Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. ...

  4. jquery ajax例子

    (1)取得服务端当前时间 jquery对象.load(url,sendData,function(backData,textStatus,xhr){... ...}) load():如果无参的话,就以 ...

  5. 【LeetCode】71. Simplify Path

    Simplify Path Given an absolute path for a file (Unix-style), simplify it. For example,path = " ...

  6. [Python]网络爬虫(七):Python中的正则表达式教程(转)

    接下来准备用糗百做一个爬虫的小例子. 但是在这之前,先详细的整理一下Python中的正则表达式的相关内容. 正则表达式在Python爬虫中的作用就像是老师点名时用的花名册一样,是必不可少的神兵利器. ...

  7. Android 判断SD卡是否存在及容量查询

    Android 判断SD卡是否存在及容量查询的简单方法如下: 首先要在AndroidManifest.xml中增加SD卡访问权限 <!-- 在SDCard中创建与删除文件权限 --> &l ...

  8. 从错误中学python(4)——最小公约数与辗转相除法

    题目 给你两个正整数a和b, 输出它们的最大公约数 辗转相除法 辗转相除法的步骤 def gcd(b,a): b,a=a,b%a if a==0: return b else: return gcd( ...

  9. 解决Mac OS X 升级10.10(Yosemite)后ADT(Eclipse)无法找到真机

    升级Yosemite之后继续开发Android,发现当编译版本高于设备版本的时候设备选择器找不到真机了,WTF!难道要改低版本SDK开发不成?臣妾做不到啊! 后来捣鼓了一阵,发现解决方案有两个: 1. ...

  10. node 服务器开发必备神器 —— nodemon

    nodemon 官方网站:http://nodemon.io/ github地址:https://github.com/remy/nodemon/ 简介:Nodemon 是一款非常实用的工具,用来监控 ...