PHP内核之旅-5.强大的数组
PHP 内核之旅系列
一、数组的内部结构
1.底层实现为散列表(HashTable,也称作哈希表)
2.散列表的概念:
是根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。复杂度为O(1)。
文件路径\Zend\zend_types.h
_zend_array结构:
typedef struct _zend_array zend_array;
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 consistency)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
zend_array和HashTable结构相同
arData:散列表中保存存储元素的数组,其内存是连续的,arData指向数组的起始位置,其内存连续。
nTableSize:数组的总容量,可以容纳的元素数,大小是2的幂次方,最小为8
nTableMask: 映射元素的存储位置用到,nTableSize的负数
nNumUsed: 数组当前使用的Bucket数,删除元素时,不会将其从数组中移除,将这个元素的类型标为IS_UNDEF,当数组容量超限,扩容时才会删除。
nNumOfElements:数组中有效元素的位置
nNextFreeElement:下一个数值的索引
pDestructor:删除或覆盖数组中的某个元素时,则调用此函数对旧元素进行处理
u:辅助作用
Bucket结构:
typedef struct _Bucket {
zval val;
zend_ulong h; /* hash value (or numeric index) */
zend_string *key; /* string key or NULL for numerics */
} Bucket;
val: 具体的value
h: key的hash值,或者数值索引
*key: 存储元素的key,如果元素是数值索引则为NULL
二、数组的基本实现
散列函数:将元素进行hash运算后的值,对数组大小取模之后的值(下标:0~7)分配到中间映射表
中间映射表:元素和下标的映射关系表。可以通过arData向前访问到
元素数组:实际存储元素的数组,按照下标有序存储元素
三、数组的初始化
\Zend\zend_hash.c
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
{
GC_REFCOUNT(ht) = 1;
GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
ht->nTableMask = HT_MIN_MASK;
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nInternalPointer = HT_INVALID_IDX;
ht->nNextFreeElement = 0;
ht->pDestructor = pDestructor;
ht->nTableSize = zend_hash_check_size(nSize);
}
\Zend\zend_API.c
ZEND_API int _array_init(zval *arg, uint32_t size ZEND_FILE_LINE_DC) /* {{{ */
{
ZVAL_NEW_ARR(arg);
_zend_hash_init(Z_ARRVAL_P(arg), size, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
return SUCCESS;
}
四、插入
插入时首先会检查数组已经分配存储空间,初始化时没有实际分配arData的内存,第一次插入时才会根据nTableSize的大小分配,分配完以后会把HashTable->u.flags打上HASH_FLAG_INITIALIZED掩码,下次插入时发现已经分配了就不会再重复操作。
static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
{
HT_ASSERT_RC1(ht);
if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) {
zend_hash_real_init_ex(ht, packed);
}
} #define CHECK_INIT(ht, packed) \
zend_hash_check_init(ht, packed)
参考资料:
http://www.php-internals.com/
PHP7内核剖析
作 者:
Jackson0714
出 处:http://www.cnblogs.com/jackson0714/
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
PHP内核之旅-5.强大的数组的更多相关文章
- PHP内核之旅-2.SAPI中的Cli
PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli 一.SAPI是什么? 1.1 理解SAPI (1)SAPI是PHP框架的接口层.有很多种服务器的SAPI的实 ...
- PHP内核之旅-3.变量
PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 一.弱类型语言 php是弱类型语言.一个变量可以表示任意数据类型. php强大的一 ...
- PHP内核之旅-6.垃圾回收机制
回收PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6 ...
- PHP内核之旅-4.可变长度的字符串
PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6.垃 ...
- JAVA之旅(三)——数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想
JAVA之旅(三)--数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想 我们继续JAVA之旅 一.数组 1.概念 数组就是同一种类型数据的集合,就是一个容器 数组的好 ...
- MOOC Linux内核之旅小结【转】
转自:https://blog.csdn.net/titer1/article/details/45345123 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csd ...
- Linux内核之旅
http://www.kerneltravel.net/ Linux内核之旅 Linux Kernel Travel
- PHP强大的数组函数
<?php/** * Created by PhpStorm. * User: 63448 * Date: 2018/5/6 * Time: 21:56 */echo "<h3& ...
- 【Swift学习】Swift编程之旅---集合类型之数组(六)
swift提供了3种主要的集合类型,array,set,dictionary.本节介绍array. 数组是存储有序的相同类型的集合,相同的值可以多次出现在不同的位置. 注意: swift的Array类 ...
随机推荐
- 2. 网友对app后端写作系列文章的写作建议
很感谢"app后端"qq群的网友,在发布消息后,就收到了大量网友的反馈 下面的建议会融入到写作当中: 1.还有,对版本升级很感兴趣,我们现在为了兼容旧版本,已经把工程代码搞的乱哄哄 ...
- Spring IOC知识点一网打尽!
前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总 ...
- Golang 通用连接池库 Golang-Pool
Golang 实现的连接池 功能: * 连接池中连接类型为interface{},使得更加通用 * 链接的最大空闲时间,超时的链接将关闭丢弃,可避免空闲时链接自动失效问题 * 使用channel处理池 ...
- ISCC 2018 (Please give me username and password)
做过iscc 2018之后有了很多的感触,也有更多的了解自己的不足之处,整理了一下web的wp, 为了保证各位小伙伴的阅读质量,我将会把wp以每一道题一个博文的形式写出来,希望能够帮助到你们 其中的步 ...
- 列表(list) ----python
Python 列表(List) 序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型 ...
- h5仿微信聊天(高仿版)、微信聊天表情|对话框|编辑器
之前做过一版h5微信聊天移动端,这段时间闲来无事就整理了下之前项目,又重新在原先的那版基础上升级了下,如是就有了现在的h5仿微信聊天高仿版,新增了微聊.通讯录.探索.我四个模块 左右触摸滑屏切换,聊天 ...
- 聊一聊C# 8.0中的await foreach
AsyncStreamsInCShaper8.0 很开心今天能与大家一起聊聊C# 8.0中的新特性-Async Streams,一般人通常看到这个词表情是这样. 简单说,其实就是C# 8.0中支持aw ...
- Gradle中的闭包
Gradle是基于Groovy的DSL基础上的构建工具,Gradle中的闭包,其原型上实际上即Groovy中闭包.而在表现形式上,其实,Gradle更多的是以约定和基于约定基础上的配置去展现.但本质上 ...
- Java 泛型完全解读
对于泛型的使用我想大家都非常熟悉,但是对于类型擦除,边界拓展等细节问题,可能不是很清楚,所以本文会重点讲解一下:另外对泛型的了解其实可以看出,一个语言特性的产生逻辑,这对我们平时的开发也是非常有帮助的 ...
- OO第二单元作业小结
前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...