typedef struct _Bucket
{
char *key;
void *value;
struct _Bucket *next;
} Bucket; typedef struct _HashTable
{
int size; // 哈希表的大小
int elem_num; // 已经保存元素的个数
Bucket **buckets;
} HashTable;

在 HashTable 里面存放这 Bucket 的指针数组,而 Bucket 本身的`*next`是用来解决 hash 冲突的,一个 key 下面对应多个值的情况,就放在一个链表上。

画了我好久,下面这张图

看到一篇文章,正好就是把 php 里面的 hashtable 给抽出来了:http://liuzhiqiangruc.iteye.com/blog/1871334

更加具体详细的介绍:http://www.php-internals.com/book/?p=chapt03/03-01-02-hashtable-in-php

下面是自己的笔记:

php 里所有的变量都存在了结构体_zval_struct中:

 struct _zval_struct {
zvalue_value value; // 存储的变量的值
zend_unit refcount__gc; // 表示引用的次数 默认为1
zend_uchar type; // 变量具体的类型 IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一
zend_uchar is_ref_gc; // 表示是否为引用 默认为0
}

从上面的可以看出,存储的值又存储在联合体`zvalue_value`中:

  1. typedef union _zvalue_value {
    long lval; /* long value */
    double dval; /* double value */
    struct {
    char *val;
    int len;
    } str;
    HashTable *ht; /* hash table value */
    zend_object_value obj;
    } zvalue_value;

而在 php 中数组实际在`_zvalue_value`里存储的就是HashTable. 上面定义的简单的 hashtable 不足于满足 php 实际复杂业务逻辑的需求,我们需要的功能很多,比如count,array_pop,next,pre,current等。所以为了让这些操作更加高效,就选择了用空间换时间的做法,存储更多的字段,存储更多的关联关系。

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 pDestructor; // 在删除元素时执行的回调函数,用于资源的释放
zend_bool persistent; //指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。
unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;

然后 bucket 则如果有 hash 冲突,使得其相互之间是一个双向链表的结构

 typedef struct bucket {
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的上一个元素
// 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体
char arKey[1];
} Bucket;

PHP 内部的 hashtable 的示意图(来源于网络):

PHP5.3 里面数组的的实现方式的更多相关文章

  1. JavaScript中清空数组的三种方式

    方式1,splice ? 1 2 3 var ary = [1,2,3,4]; ary.splice(0,ary.length); console.log(ary); // 输出 [],空数组,即被清 ...

  2. jquery取对象数组元素的错误方式

    代码如下: <div id="div1"> <span>a</span> <span>b</span> <span ...

  3. java8 遍历数组的几种方式

    java8 遍历数组的几种方式 2017年04月05日 09:15:46 阅读数:4640 风格色 2017-02-11 18:41 有如下一个String数组 String[] array = {& ...

  4. Java Array数组 遍历 四种方式(包含 Lambda 表达式遍历)

    示例代码如下: package com.miracle.luna.lambda; import java.util.Arrays; /** * @Author Miracle Luna * @Date ...

  5. 五分钟学Java:打印Java数组最优雅的方式是什么?

    在逛 Stack Overflow 的时候,发现了一些访问量像‎安第斯山一样高的问题,比如说这个:打印 Java 数组最优雅的方式是什么?访问量足足有 220W+,想不到啊,这么简单的问题竟然有这么多 ...

  6. C#初始化数组的三种方式

    C#声明数组并初始化,有三种方式. 对于一维数组: using System;using System.Data;using System.Configuration;using System.Web ...

  7. 如何随机排序数组?使用多种方式!递归,迭代,洗牌,sort方法!

    方式1: 使用sort 方法 ---- // 方法1 使用sort 方法 var arr = [1,2,3,4,5,6,7,8]; function foo(arr) { var cloneArr = ...

  8. 【Java】数组转List常见方式的对比

    一.最常用通过 Arrays.asList(strArray) 方式,将数组转换List后,不能对List增删,只能查改,否则抛异常. 关键代码: List list = Arrays.asList( ...

  9. VBA遍历数组的2种方式

      1.情景展示 VBA编程,如何对数组进行遍历? 2.解决方案 方式一:使用for循环 Sub 遍历数组1() '声明一个变量 Dim Arr As Variant '声明一个数字变量 Dim i ...

随机推荐

  1. 引擎介绍 - REngine

    引擎介绍 - REngine 规则引擎 规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策.接受数据输入,解释业务规 ...

  2. 微软不也是从Altair Basic这丑小鸭长成白天鹅吗?

    微软不也是从Altair Basic这丑小鸭长成白天鹅吗? February 2015 如果你想要弄清楚初创企业是怎么一回事的话,其中一个非常有价值的尝试是去研究下那些获得巨大成功的公司,去分析下为什 ...

  3. ArrayList/List 泛型集合

    List泛型集合 集合是OOP中的一个重要概念,C#中对集合的全面支持更是该语言的精华之一. 为什么要用泛型集合? 在C# 2.0之前,主要可以通过两种方式实现集合: a.使用ArrayList 直接 ...

  4. ECharts图表系统 特性总览

    最近在玩ECharts,感觉真心不错,在这里把官方的资料收集收集,给大家推荐一下下~ Architecture ECharts (Enterprise Charts 商业产品图表库) 提供商业产品常用 ...

  5. .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器

    .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器 北京时间今天凌晨的 Connect(); 大会上,多少程序员的假想成为现实. ...

  6. 批处理中set截取字符具体解释

    set截取字符具体解释  在批处理中,set的功能有点繁杂:设置变量.显示环境变量的名及值.做算术运算.等待用户的输入.字符串截取.替换字符串,是我们经常使用的命令之中的一个. 在字符串截取方面,新手 ...

  7. Retrofit相关资料

    高速Android开发系列网络篇之Retrofithttp://www.w3c.com.cn/%E5%BF%AB%E9%80%9Fandroid%E5%BC%80%E5%8F%91%E7%B3%BB% ...

  8. Android项目----dispathTouchEvent

    说到dispathTouchEvent,就不得不说一个最贱的屏幕触摸动作触发的一些列Touch事件: ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->A ...

  9. ASP.NET MVC IOC 之AutoFac

    ASP.NET MVC IOC 之AutoFac攻略 一.为什么使用AutoFac? 之前介绍了Unity和Ninject两个IOC容器,但是发现园子里用AutoFac的貌似更为普遍,于是捯饬了两天, ...

  10. DDD 回归具体的业务场景,Domain Model 再再重新设计

    DDD 回归具体的业务场景,Domain Model 再再重新设计 首先,把最真挚的情感送与梅西,加油! 写在前面 阅读目录: 重申业务场景 Domain Model 设计 后记 上一篇<设计窘 ...