网站首页 > 技术教程 正文
PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。--PHP手册
数组是PHP中非常强大、灵活的一种数据类型,它的底层实现为HashTable(哈希表),除了我们熟悉的PHP用户空间的Array类型之外,内核中也随处用到哈希表,比如函数、类、常量、include文件的索引表、全局符号表等都用的HashTable存储。
数组结构
PHP中HashTable的数据结构如下:
//Bucket:散列表中存储的元素 typedef struct _Bucket { zval val; //存储的具体value,这里嵌入了一个zval,而不是一个指针 zend_ulong h; //key根据times 33计算得到的哈希值,或者是数值索引编号 zend_string *key; //存储元素的key } Bucket; //HashTable结构 typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar reserve) } v; uint32_t flags; } u; uint32_t nTableMask; //哈希值计算掩码,等于nTableSize的负值(nTableMask = -nTableSize) Bucket *arData; //存储元素数组,指向第一个Bucket uint32_t nNumUsed; //已用Bucket数 uint32_t nNumOfElements; //哈希表有效元素数 uint32_t nTableSize; //哈希表总大小,为2的n次方 uint32_t nInternalPointer; zend_long nNextFreeElement; //下一个可用的数值索引,如:arr[] = 1;arr["a"] = 2;arr[] = 3; 则nNextFreeElement = 2; dtor_func_t pDestructor; };
注意:nNumUsed:已用bucket数 nNumOfElements:哈希表有效元素数
两者有不同的含义,当将一个元素从哈希表删除时并不会将对应的Bucket移除,而是将Bucket存储的zval修改为IS_UNDEF,只有扩容时发现nNumOfElements与nNumUsed相差达到一定数量时才会将已删除的元素全部移除,重新构建哈希表,所以 nNumUsed >= nNumOfElements。
HashTable中另外一个非常重要的值arData,这个值指向存储元素数组的第一个Bucket,插入元素时按顺序依次插入数组,比如第一个元素在arData[0]、第二个在arData[1] ... arData[nNumUsed]。PHP数组的有序性正是通过arData保证的。
所以,HashTable主要依赖arData实现元素的存储、索引。插入一个元素时先将元素按先后顺序插入Bucket数组,位置是idx,再根据key的哈希值映射到散列表中的某个位置nIndex,将idx存入这个位置;查找时先在哈希表中映射到nIndex,得到value在Bucket数组的位置idx,再从Bucket数组中取出元素。
比如:
对应的HashTable如下图所示:
哈希碰撞
哈希碰撞是指不同的key可能计算得到相同的哈希值(数值索引的哈希值直接就是数值本身),但是这些值又需要插入同一个哈希表。
出现哈希碰撞之后,又如何解决呢?一般有如下几种方法:
- 链表法:为每个 Hash 值建立一个单链表,当发生冲突时,将记录插入到链表中。
- 开放寻址法:开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1),基本思想:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。
- 再哈希法:当发生冲突时,使用第二个、第三个哈希函数计算地址,直到无冲突时。
扩容
哈希表可存储的value数是固定的,当空间不够用时就要进行扩容了。
PHP哈希表的大小为2^n,插入时如果容量不够则首先检查已删除元素所占比例,如果达到阈值时,则将已删除元素移除,重建索引;如果未到阈值则进行扩容操作,扩大为当前大小的2倍,将当前Bucket数组复制到新的空间,然后重建索引。
重建哈希表
当删除元素达到一定数量或扩容后就需要重建哈希表,因为value在Bucket位置移动了或哈希数组nTableSize变化了导致key与value的映射关系改变。
重建过程实际就是遍历Bucket数组中的value,然后重新计算映射值更新到哈希表。
此外,还有一个重要的处理:移除已删除的value,开始的时候我们说过,删除value时只是将value的type设置为IS_UNDEF,并没有实际从Bucket数组中删除,如果这些value一直存在那么将浪费很多空间,所以这里会把它们移除,操作的方式也比较简单:将后面未删除的value依次前移。
以上,介绍了数组的哈希结构,如有遗漏和不足欢迎大家继续补充。
- 上一篇: 选择美国免费空间需要注意什么?(美国空间网址)
- 下一篇: 新手小白如何用8小时搭建一个影视网站!
猜你喜欢
- 2024-10-21 常见面试问题之-设计模式 重要包含PHP代码实现额,PHP开发者必看
- 2024-10-21 php生命周期介绍(php的生命周期, 启动流程, 多看tipi)
- 2024-10-21 Laravel框架命名空间的定义(laravel框架关键技术解析)
- 2024-10-21 网站建站流程-空间购买(网站空间商选择考虑三大因素)
- 2024-10-21 准确JavaScript世界观:复杂值vs原始值&&内存空间(1)
- 2024-10-21 如何搭建个人博客网站?角点科技教你搭建博客网站
- 2024-10-21 建设属于自己的网站需要什么(建设属于自己的网站需要什么资质)
- 2024-10-21 省钱了!使用极空间搭建一个免费的MariaDB数据库并实现远程访问
- 2024-10-21 Windows+Apache+MySQL+PHP4 官方推荐详细安装步骤
- 2024-10-21 PHP内存管理机制与垃圾回收机制(php 内存模型)
你 发表评论:
欢迎- 最近发表
-
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
- linux移植(Linux移植freemodbus)
- 独家解读:Win10预览版9879为何无法识别硬盘
- 基于Linux系统的本地Yum源搭建与配置(ISO方式、RPM方式)
- Docker镜像瘦身(docker 减小镜像大小)
- 在linux上安装ollama(linux安装locale)
- 渗透测试系统Kali推出Docker镜像(kali linux渗透测试技术详解pdf)
- Linux环境中部署Harbor私有镜像仓库
- linux之间传文件命令之Rsync傻瓜式教程
- 解决ollama在linux中安装或升级时,通过国内镜像缩短安装时长
- 标签列表
-
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
本文暂时没有评论,来添加一个吧(●'◡'●)