PHPer 高手之路
  • Introduction
  • First Chapter
  • 基础
    • 数据类型和常量
    • 字符串
      • 字符编码
      • 字符编码相关编程
    • 引用变量
    • 运算符与错误控制符@
    • 流程控制与条件判断
      • foreach遍历中的引用
    • 函数
    • 文件及目录处理
  • PHP 数组
    • 基础
    • PHP数组操作
    • 输入流 php://input
    • PHP数组的内部实现
    • PHP数组和数据结构
    • 示例技巧
  • PHP文件编程
    • 文件系统
    • 基础
    • 实例技巧
    • PHP中XML处理
    • PHP中JSON处理
    • PHP中CSV处理
    • 大文件上传
  • 正则表达式
    • 基础
    • 正则的引擎
    • 表达式的优化
    • PHP中正则的使用
  • PHP 编码技巧
    • PHP编码习惯
    • PHP语法糖
    • PHP代码优化
    • PHP重点新特性
    • PHP编码规范
  • PHP选项和运行原理
    • PHP SAPI
    • PHP运行模式及安装方式
    • 附录:进程和线程的Q解
    • Apache下的MPM模式
    • Apache 与 Nginx
    • PHP的运行机制及原理
    • PHP垃圾回收机制
    • PHP配置选项
  • PHP安全
    • 跨站脚本攻击(XSS )
    • 跨站请求伪造(CRSF)
  • PHP 高级特性
    • 异常处理(Exceptions)
    • 代码复用(Trait)
    • 预定义接口(Predefined Interfaces)
    • 魔术方法(Magic Methods)
    • 回调函数、匿名函数&闭包
    • 命名空间(Namespaces)
    • 自动加载(Autoload)
    • 反射(Reflection)
    • 魔术常量(Magic constants)
    • 综合实例
  • 附录:关键词
  • 附录:资料
  • 代码的版本控制
    • SVN
    • Git
      • 疑难杂症
  • Linux
    • Linux原理与基础
    • 常见命令
    • Shell 编程
    • awk 与 sed
    • 命令笔记
  • HTTP 协议
    • 请求方法与返回状态码
    • Cookie、Session 的原理
  • MySQL
    • MySQL表存储引擎
  • 标准PHP库(SPL)
    • 数据结构
      • SplPriorityQueue - 优先队列
      • SplQueue - 队列
      • SplStack - 栈的功能
    • 接口
      • Countable - count统计接口
  • 附录:ElasticSearch
  • PHP数据结构
  • 附录:Rabbitmq
  • 附录:guzzle
  • JavaScript
    • 附录:资料
  • 疑难杂症
Powered by GitBook
On this page
  • 什么是HashTable ?
  • PHP数组Hashtable结构体
  • Bucket结构体
  • hash key
  • HashTable扩容
  • prev() 的指针实现

Was this helpful?

  1. PHP 数组

PHP数组的内部实现

Previous输入流 php://inputNextPHP数组和数据结构

Last updated 5 years ago

Was this helpful?

  • 实现数组使用了两个数据结构,一个是HashTable,另一个是bucket。

  • HashTable结构体用于保存整个数组需要的基本信息。

  • Bucket结构体用于保存具体的数据内容。

什么是HashTable ?

哈希表,是根据关键字(Key value)而直接访问在内存存储位置的数据结构。也就是说,它通过把键值通过一个函数的计算,映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。

哈希表

  • hash table,又叫哈希表,散列表,Hash表。

  • 这种数据结构通过key->value的映射关系,使得普通的查找和插入、删除操作都可以在O(1) 的时间内完成。

  • key->value的映射是通过Hash函数来实现的。

PHP数组Hashtable结构体

typedef struct _hashtable {
    uint nTableSize;        // hash Bucket的大小,最小为8,以2x增长。
    uint nTableMask;        // nTableSize-1 , 索引取值的优化,193491849 & 127
    uint nNumOfElements;    // hash Bucket中当前存在的元素个数,count()函数会直接返回此值。
    ulong nNextFreeElement; // 下一个数字索引的位置
    Bucket *pInternalPointer;   // 当前遍历的指针foreach比for快的原因之一,reset, current遍历函数使用
    Bucket *pListHead;          // 存储数组头元素指针
    Bucket *pListTail;          // 存储数组尾元素指针
    Bucket **arBuckets;         // 存储hash数组,实际的存储容器
    unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
} HashTable;

nTableSize : 表长度,并非元素个数

nTableMask:表的掩码,始终等于nTableSize-1

nNumOfElements:存储的元素个数

nNextFreeElement:指向下一个空的元素位置

pInternalPointer:foreach循环时,用来记录当前遍历到的元素位置

Bucket结构体

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;   // 整个hash表该元素的上一个元素
    struct bucket *pNext;       // 存放在同一个hash Bucket内的下一个元素
    struct bucket *pLast;       // 同一个hash bucket的上一个元素
    const char *arKey;          // 保存当前key所对应的字符串值
} Bucket;

hash key

typedef struct _zend_hash_key {
    const char *arKey;
    uint nKeyLength;
    ulong h;
} zend_hash_key;

数字索引

直接使用h作为hash值。

arKey=NULL 且nKeyLength=0

字符串索引(关联数组)

h则是该字符串通过hash函数计算后的hash值。

arKey保存字符串key,

nKeyLength保存该key的长度,

在PHP数组中如果索引字符串可以被转换成数字也会被转换成数字索引。

所以在PHP中例如'10','11'这类的字符索引和数字索引10, 11没有区别。

HashTable扩容

HashTable的大小并不是固定不变的,当nNumOfElements > nTableSize时,会对HashTable进行扩容,以便于容纳更多的元素。

扩容之后需要对原来hashTable的元素rehash。

<?php 
$a = array();
$p = (1 << 14) - 1; //每一次移动都表示"乘以 2"
$b = 1;
for ($i = 0; $i < $p; $i++) {
    $a[] = $b;
}
echo memory_get_usage(true) . "\n";
$a['as1'] = 1;
echo memory_get_usage(true) . "\n";
$a['as2'] = 1;
echo memory_get_usage(true) . "\n";

prev() 的指针实现

  • HashTable结构体中,有一个成员pInternalPointer, 这个成员便是控制数组的访问指针的。

  • 先找到数组的当前位置或指针

  • 然后访问这个指针的pListLast找到上一个元素:

  • 由于访问指针已经移动到了适当的位置,则直接获取当前指针指向的元素。