php Hash Table(一) Hash Table的结构

Hash Table的结构图:

在上图中发现:Bucket1和Bucket2是hash冲突的双向链表,但是后添加的Bucket2是添加到头部的,可以看到Bucket2的pListLast和pNext指向Bucket1。

对HashTable结构体的字段解释:

1.nTableSize。顾名思义这个是整个哈希表分配的大小(在内部实现的C中分配的数组大小,PHP是动态的但到底层数组是有大小的是静态的),他的大小有一个固定的申请算法,一般是最接近并且大于当前这个数值的2的乘方,描述的可能有点模糊,举个例子来看,如果PHP数组存储32个整形数据,那么底层申请的nTableSize应该等于32个元素,如果33呢,那么取最近且大于这个数的一个数64,那么分配的大小是64个元素。这样分配的原因是为了能分配足够的内存同样又不会浪费太多的内存。基于哈希的效率考虑,太小那么势必造成哈希之后太多的碰撞查找,如果分配太大那么必然浪费太多内存,这样分配经过实践证明相对在空间和时间上可以获得一个平衡。

2.nTableMask。哈希表的掩码数值等于nTableSize-1,他的作用是什么?用来纠正通过DBJ算法计算的哈希值在当前nTableSize大小的哈希表中的正确的索引值。比如"foo"通过固定算法之后得出的哈希值是193491849,如果表的大小为64,很明显已经超过了最大索引值,这时候就需要运用哈希表的掩码对其进行矫正实际采用的方法就是与掩码进行位与运算,这样做是为了把哈希值大的一样映射到nTalbeSize空间内。

   hash  |   193491849 |   0b1011100010000111001110001001
 & mask  | &        63 | & 0b0000000000000000000000111111
---------------------------------------------------------
 = index | =         9 | = 0b0000000000000000000000001001

3.nNumOfElements。是PHP数组中实际存储元素的个数,我们使用count,sizeof计算的就是获取的这个值。

4.nNextFreeElement。下一个空闲的元素空间,当我们申请一个空下标元素的时候就需要用到此项,比如$ret[] = ‘apple‘。

5.pInternalPointer。存储了内部当前执行的元素的指针,当我们使用一些内部循环函数的时候会用到这个指针比如reset(), current(), prev(), next(), foreach(), end()。

6.pListHead和pListTail则具体指向了该哈希表的第一个和最后一个元素,对应就是数组的起始和结束元素。

7.arBuckets。这个就是实际存储的C的内部数组。这里记录的是一个指向指针的指针Bucket **。即指向一个指针数组,其中每个元素是一个指向Bucket链表的头指针。

8.pDestructor 是一个析构函数,当某个值被从哈希表删除的时候会触发此函数。他还有一个主要作用是用于变量的GC回收。在PHP里面GC是通过引用计数实现的,当一个变量的引用计数变为0,就会被PHP的GC回收。

9.persistent 定义了hashtable是否能在多次request中获得持久存在。

10.nApplyCount 和 bApplyProtection 是用来防止无限递归的。

11.inconsistent 是在调试模式下捕获对HT不正确的使用。

在zend/Zend_hash.h中对hashtable的定义:

typedef struct _hashtable {
    uint nTableSize;        // hash Bucket的大小,最小为8,以2x增长。
    uint nTableMask;        // nTableSize-1 , 掩码,用于根据hash值计算存储位置。
    uint nNumOfElements;    // hash Bucket中当前存在的元素个数,count()函数会直接返回此值
    ulong nNextFreeElement; // 下一个数字索引的位置,$arr[] = "hello"时会用到
    Bucket *pInternalPointer;   // 当前遍历的指针(foreach比for快的原因之一)
    Bucket *pListHead;          // 存储数组头元素指针
    Bucket *pListTail;          // 存储数组尾元素指针
    Bucket **arBuckets;         // 存储hash数组
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
    zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;

 对Bucket结构体字段的解释:

1.h是一个哈希值,未经过掩码矫正的哈希DBJ算出来的原始值。

2.arKey,用来记录作为哈希计算的字符串,nKeyLength是哈希字符串的长度,对于整形键值是用不到这两项的。

3.pData以及pDataPtr是实际存储数据的指针,在PHP里面他们通常是指向一个zval结构(该结构广泛被PHP用来内部存储各种变量以及对象)。

4.pListNext, pListLast 指定了整个数组的顺序,PHP中的遍历就是通过哈希结构体中的pListHead bucket依次遍历pNext直到数组结束。

5.pNext和pLast 这两个指针是用来解决哈希冲突的,这个在下面哈希冲突中详细介绍,在PHP的哈希表冲突的处理采用的是拉链法也就是在每个可能冲突的键值位置拉出一个链表来存储对应的键值数据。

在zend/Zend_hash.h中对bucket的定义:

typedef struct bucket {
    /* Used for numeric indexing */
    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的上一个元素
    char arKey[1];
    /*存储字符索引,此项必须放在最未尾,因为此处只字义了1个字节,存储的实际上是指向char *key的值,
    这就意味着可以省去再赋值一次的消耗,而且,有时此值并不需要,所以同时还节省了空间。
    */
} Bucket;
时间: 2024-11-05 17:28:09

php Hash Table(一) Hash Table的结构的相关文章

OpenFlow Switch学习笔记(五)——Group Table、Meter Table及Counters

本文主要详述OpenFlow Switch的另外两个主要组件——Group Table和Meter Table,它们在整个OpenFlow Swtich Processing中也起到了重要作用. 1.Group Table Group Table给OpenFlow Switch提供了更加高级的数据包转发特性(比如select或者all),其由多个Group Entries组成,而每个Group Entry结构如下所示: 每个Group Entry根据其Group Identifier来唯一定位,

hash表、hash算法

概念: 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表.给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数 散列函数的选取: 1. 直接寻址法:取关键字或关键字的某个线性函数值为散列地址

Hash::make与Hash::check

调用方法之前要先去引用: use Illuminate\Support\Facades\Hash; 可以调用 Hash 门面上的 make 方法对存储密码进行哈希: $pwd = Hash::make($request->newPassword); //加密存储 check 方法允许你验证给定原生字符串和给定哈希是否相等 if (Hash::check('qwe123456', $pwd)) { // 密码匹配... } 举例子: 1,Hash::make()存储数据2,从数据库获取所有数据,然

查询计划Hash和查询Hash

查询计划hash和查询hash 在SQL Server 2008中引入的围绕执行计划和缓冲的新功能被称为查询计划hash和查询hash.这是使用针对查询或查询计划的算法来生成二进制hash值的二进制对象. 可以从sys.dm_exec_query_stats或sys.dm_exec_requests检索查询计划hash和查询hash.虽然这是确认查询及其计划的一种机制,但是hash值不是唯一的.不相似的查询可能得出相同的hash,所以不能将其作为备份主键. 分别创建两个查询如下: SELECT

css Table布局-display:table

使用表格布局一直是一个敏感的主题.一般情况下,Web开发人员考虑基于表格布局是禁忌.尽管反对的理由看起来证据很充分,但是大多数开发者除了谴责基于表格的布局,都无法提供完善的使用场景."表格不好." 从早期反对HTML Table(<table>标签)开始这种势头就非常强劲.几代开发者被成功洗脑,根深蒂固的认为:任何使用表格都是邪恶的. 诚然,我也是避免使用表格布局的开发者之一,即使是显示表格数据. 我甚至曾经斥责我的下属开发者同事,当他们使用 display:table 用

JDBC访问Oracle数据库例子源代码,包括创建table,删除table,插入记录,删除记录,查询记录等

package com.cb; public class SMSInfo { public static String ITEMINDEX = "sms_index"; public static String ITEMTO = "sms_to"; public static String ITEMFROM = "sms_from"; public static String ITEMMSG = "sms_msg"; publ

show table status like &#39;table&#39;\G 详细信息介绍

mysql> show table status like'leyangjun'\G *************************** 1. row *************************** Name: leyangjun                 表名字 Engine: MyISAM                 表存储引擎 Version: 10                          版本 Row_format: Dynamic        行格式

Oracle drop table 和 truncate table对grant授权的影响

1.以sys登陆,建表赋予权限,准备测试表z2 [[email protected] ~]$ rlwrap sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Tue May 16 14:59:27 2017 Copyright (c) 1982, 2013, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition

转载:字符串hash总结(hash是一门优雅的暴力!)

转载自:远航休息栈 字符串Hash总结 Hash是什么意思呢?某度翻译告诉我们: hash 英[hæ?] 美[hæ?]n. 剁碎的食物; #号; 蔬菜肉丁;vt. 把…弄乱; 切碎; 反复推敲; 搞糟; 我觉得Hash是引申出 把...弄乱 的意思. 今天就来谈谈Hash的一种——字符串hash. 据我的理解,Hash就是一个像函数一样的东西,你放进去一个值,它给你输出来一个值.输出的值就是Hash值.一般Hash值会比原来的值更好储存(更小)或比较. 那字符串Hash就非常好理解了.就是把字符