search
Homephp教程php手册PHP与Python实现Hash比较(一)

PHP中的array,python中的dict都是通过hash表(哈希表或散列表)实现的,或者说array与dict本身就是hash结构,本文及后续文章将分别比较PHP与python源代码中对哈希表的实现算法,一来学习其设计思想,另外可用于避免开发过程中一些可能会降低效率或易引发bug的

PHP中的array,python中的dict都是通过hash表(哈希表或散列表)实现的,或者说array与dict本身就是hash结构,本文及后续文章将分别比较PHP与python源代码中对哈希表的实现算法,一来学习其设计思想,另外可用于避免开发过程中一些可能会降低效率或易引发bug的操作。

先来PHP。一切源于PHP的内置数据类型zval(见PHP_X_X/Zend/zend.h):

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;
struct _zval_struct {
    //Variable information
    zvalue_value value;     //value
    zend_uint refcount_gc;
    zend_uchar type;    //active type
    zend_uchar is_ref_gc;
};

其中HashTable *ht即是PHP中用于表示Array类型的结构,在深究HashTable结构之前先了解哈希表的原理,在C语言中数组是通过自然数作为数组索引来存储数据的,而在PHP或python等这些语言中,哈希表是以key - value的方式存取的,要实现这一存储方式,则需要将任意可能的key对应或映射到数组或者内存的自然数序列索引上,即实现

index = hash(key)

hash()即为哈希函数。理想状态下的hash()可以将任意的key映射到均匀分布且不重叠的自然数集合中,但由于key的不确定性,这显然是不可能的,因而一个好的哈希函数应该可以尽可能地避免重叠或碰撞(collisions),而在PHP中实现这一功能的哈希函数采纳的是DJBX33A算法。源码中实现代码如下:

static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength)
{
    register ulong hash = 5381;
    /* variant with the hash unrolled eight times */
    for (; nKeyLength >= 8; nKeyLength -= 8) {
        hash = ((hash 
<p>据其注释中所解释的来看,DJBX33A (Daniel J. Bernstein, Times 33 with Addition)算法可简单描述为</p>
<blockquote>
<p>hash(i) = hash(i-1) * 33 + str[i]</p>
</blockquote>
<p>至于为何取33而不是其它数,解释说是对1 ~ 256进行分别进行测试后择优选择的结果,并没有理论上的支撑,而且初始的hash值为5381应该也没有什么特别特别的特别之处吧?到这里为止,首先可以确定的一条规则就是,<span style="color: #3498db">在PHP中定义使用数组时key的长度以最好不要超过7为妙</span>,便可省掉第一步的for循环,因而在考虑效率的前提下,道长当年所说的为了增加代码的可读性将变量名定为几十个字符甚至一句话显然是不可取的咯:P</p>
<p>通过巧妙的算法,hash碰撞得以减少,但是并没有完全避免(例如:PHP哈希表碰撞攻击原理),既然冲突是不可避免的,那就只能想办法解决冲突,算法书里面对冲突的处理方案有很多,PHP采用的是拉链法,具体实现方法还是要先追寻其定义(见PHP_X_X/Zend/zend_hash.h):</p>
<p class="highlight"></p><pre class="brush:php;toolbar:false">typedef struct bucket {
    ulong h;                        //Used for numeric indexing
    uint nKeyLength;
    void *pData;
    void *pDataPtr;
    struct bucket *pListNext;
    struct bucket *pListLast;
    struct bucket *pNext;
    struct bucket *pLast;
    const char *arKey;
} Bucket;
typedef struct _hashtable {
    uint nTableSize;
    uint nTableMask;
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;   //Used for element traversal
    Bucket *pListHead;
    Bucket *pListTail;
    Bucket **arBuckets;
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;

最终hash表的key保存在Bucket.arKey,key长为Bucket.nKeyLength,哈希函数计算得到的哈希值存为Bucket.h,当冲突时通过引出一条静态链表来解决,其实现如下:

ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
{
    ulong h;
    uint nIndex;
    Bucket *p;
    IS_CONSISTENT(ht);
    h = zend_inline_hash_func(arKey, nKeyLength);
    nIndex = h & ht->nTableMask;
    p = ht->arBuckets[nIndex];
    while (p != NULL) {
        if (p->arKey == arKey ||
            ((p->h == h) && (p->nKeyLength == nKeyLength) 
            && !memcmp(p->arKey, arKey, nKeyLength))) {
                return 1;
        }
        p = p->pNext;
    }
    return 0;
}

p = p->pNext即在已有元素之上开辟出新的位置存储冲突的下一个元素。至此,PHP中HashTable实现的基本思想就介绍完了,有空再把python的部分补上。

构建动态结构体的小trick

Bucket结构体的最后一个元素arKey被定义为char *arKey;也有看到char arKey[1],有人解释说利用变长结构体,加上有看到注释

char arKey[1]; /* Must be last element */

更是如坠云里雾里,还以为说 arKey 必须存放 HashTable 里面 key 字符串的最后一个字符…经过一番挣扎,发现原来不是这个意思,shit!(见what-is-your-favorite-c-programming-trick),所谓的变长结构体只是说在考虑到内存连续性条件下,为了实现结构体内部元素的动态分配,利用struct的性质,将需要动态分配的变量放在结构体最后,如此以来通过malloc动态分配给struct的内存超出结构体本身所需的部分sizeof(struct)可以顺其自然地被最后一个元素所访问,从而实现了可变长的结构体,所以说,注释中的Must be last element不是说存放的是key的最后一个字符,而是必须放在结构体的最后一个元素!shit again(but a good trick:P)!

参考

  1. PHP哈希表结构的深入剖析
Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

Hot Tools

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Powerful PHP integrated development environment

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!