婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > PHP哈希表實現算法原理解析

PHP哈希表實現算法原理解析

熱門標簽:中原區電話機器人價格 招標自動語音外呼系統 黔江400電話如何辦理 地圖標注視頻廣告入駐 gps 地圖標注軟件 400電話鄭州申請 OMG地圖標注app 電銷機器人便宜的有嗎 ai電話機器人加盟代理

在PHP內核中,其中一個很重要的數據結構就是HashTable。我們常用的數組,在內核中就是用HashTable來實現。那么,PHP的HashTable是怎么實現的呢?最近在看HashTable的數據結構,但是算法書籍里面沒有具體的實現算法,剛好最近也在閱讀PHP的源碼,于是參考PHP的HashTable的實現,自己實現了一個簡易版的HashTable,總結了一些心得,下面給大家分享一下。

HashTable的介紹

哈希表是實現字典操作的一種有效數據結構。

定義

簡單地說,HashTable(哈希表)就是一種鍵值對的數據結構。支持插入,查找,刪除等操作。在一些合理的假設下,在哈希表中的所有操作的時間復雜度是O(1)(對相關證明感興趣的可以自行查閱)。

實現哈希表的關鍵

在哈希表中,不是使用關鍵字做下標,而是通過哈希函數計算出key的哈希值作為下標,然后查找/刪除時再計算出key的哈希值,從而快速定位元素保存的位置。

在一個哈希表中,不同的關鍵字可能會計算得到相同的哈希值,這叫做“哈希沖突”,就是處理兩個或多個鍵的哈希值相同的情況。解決哈希沖突的方法有很多,開放尋址法,拉鏈法等等。

因此,實現一個好的哈希表的關鍵就是一個好的哈希函數和處理哈希沖突的方法。

Hash函數

判斷一個哈希算法的好壞有以下定義:

  • 一致性,等價的鍵必然產生相等的哈希值;
  • 高效性,計算簡便;
  • 均勻性,均勻地對所有的鍵進行哈希。
  • 哈希函數建立了關鍵值與哈希值的對應關系,即:h = hash_func(key)。對應關系見下圖:

hash-exam

設計一個完美的哈希函數就交由專家去做吧,我們只管用已有的較成熟的哈希函數就好了。PHP內核使用的哈希函數是time33函數,又叫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  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
      hash = ((hash  5) + hash) + *arKey++;
  }

  switch (nKeyLength) {
    case 7: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 6: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 5: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 4: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 3: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 2: hash = ((hash  5) + hash) + *arKey++; /* fallthrough... */
    case 1: hash = ((hash  5) + hash) + *arKey++; break;
    case 0: break;
    EMPTY_SWITCH_DEFAULT_CASE()
  }
  return hash;
}

注:函數使用了一個8次循環+switch來實現,是對for循環的優化,減少循環的運行次數,然后在switch里面執行剩下的沒有遍歷到的元素。

拉鏈法

將所有具有相同哈希值的元素都保存在一條鏈表中的方法叫拉鏈法。查找的時候通過先計算key對應的哈希值,然后根據哈希值找到對應的鏈表,最后沿著鏈表順序查找相應的值。

PHP的HashTable結構

簡單地介紹了哈希表的數據結構之后,繼續看看PHP中是如何實現哈希表的。

PHP內核hashtable的定義:

typedef struct _hashtable {
     uint nTableSize;
     uint nTableMask;
     uint nNumOfElements;
     ulong nNextFreeElement;
     Bucket *pInternalPointer;
     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;
  • nTableSize,HashTable的大小,以2的倍數增長
  • nTableMask,用在與哈希值做與運算獲得該哈希值的索引取值,arBuckets初始化后永遠是nTableSize-1
  • nNumOfElements,HashTable當前擁有的元素個數,count函數直接返回這個值
  • nNextFreeElement,表示數字鍵值數組中下一個數字索引的位置
  • pInternalPointer,內部指針,指向當前成員,用于遍歷元素
  • pListHead,指向HashTable的第一個元素,也是數組的第一個元素
  • pListTail,指向HashTable的最后一個元素,也是數組的最后一個元素。與上面的指針結合,在遍歷數組時非常方便,比如reset和endAPI
  • arBuckets,包含bucket組成的雙向鏈表的數組,索引用key的哈希值和nTableMask做與運算生成
  • pDestructor,刪除哈希表中的元素使用的析構函數
  • persistent,標識內存分配函數,如果是TRUE,則使用操作系統本身的內存分配函數,否則使用PHP的內存分配函數
  • nApplyCount,保存當前bucket被遞歸訪問的次數,防止多次遞歸
  • bApplyProtection,標識哈希表是否要使用遞歸保護,默認是1,要使用

舉一個哈希與mask結合的例子:

例如,”foo”真正的哈希值(使用DJBX33A哈希函數)是193491849。如果我們現在有64容量的哈希表,我們明顯不能使用它作為數組的下標。取而代之的是通過應用哈希表的mask,然后只取哈希表的低位。

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

因此,在哈希表中,foo是保存在arBuckets中下標為9的bucket向量中。

bucket結構體的定義

typedef struct bucket {
   ulong h;
   uint nKeyLength;
   void *pData;
   void *pDataPtr;
   struct bucket *pListNext;
   struct bucket *pListLast;
   struct bucket *pNext;
   struct bucket *pLast;
   const char *arKey;
} Bucket;
  • h,哈希值(或數字鍵值的key
  • nKeyLength,key的長度
  • pData,指向數據的指針
  • pDataPtr,指針數據
  • pListNext,指向HashTable中的arBuckets鏈表中的下一個元素
  • pListLast,指向HashTable中的arBuckets鏈表中的上一個元素
  • pNext,指向具有相同hash值的bucket鏈表中的下一個元素
  • pLast,指向具有相同hash值的bucket鏈表中的上一個元素
  • arKey,key的名稱

PHP中的HashTable是采用了向量加雙向鏈表的實現方式,向量在arBuckets變量保存,向量包含多個bucket的指針,每個指針指向由多個bucket組成的雙向鏈表,新元素的加入使用前插法,即新元素總是在bucket的第一個位置。由上面可以看到,PHP的哈希表實現相當復雜。這是它使用超靈活的數組類型要付出的代價。

HashTable相關API

  • zend_hash_init
  • zend_hash_add_or_update
  • zend_hash_find
  • zend_hash_del_key_or_index

zend_hash_init

函數執行步驟

設置哈希表大小

設置結構體其他成員變量的初始值 (包括釋放內存用的析構函數pDescructor)
詳細代碼注解點擊:zend_hash_init源碼

注:

1、pHashFunction在此處并沒有用到,php的哈希函數使用的是內部的zend_inline_hash_func

2、zend_hash_init執行之后并沒有真正地為arBuckets分配內存和計算出nTableMask的大小,真正分配內存和計算nTableMask是在插入元素時進行CHECK_INIT檢查初始化時進行。

zend_hash_add_or_update

函數執行步驟

  • 檢查鍵的長度
  • 檢查初始化
  • 計算哈希值和下標
  • 遍歷哈希值所在的bucket,如果找到相同的key且值需要更新,則更新數據,否則繼續指向bucket的下一個元素,直到指向bucket的最后一個位置
  • 為新加入的元素分配bucket,設置新的bucket的屬性值,然后添加到哈希表中

如果哈希表空間滿了,則重新調整哈希表的大小

zend_hash_add_or_update

CONNECT_TO_BUCKET_DLLIST是將新元素添加到具有相同hash值的bucket鏈表。

CONNECT_TO_GLOBAL_DLLIST是將新元素添加到HashTable的雙向鏈表。

詳細代碼和注解請點擊:zend_hash_add_or_update代碼注解。

zend_hash_find

函數執行步驟

  • 計算哈希值和下標
  • 遍歷哈希值所在的bucket,如果找到key所在的bucket,則返回值,否則,指向下一個bucket,直到指向bucket鏈表中的最后一個位置

詳細代碼和注解請點擊:zend_hash_find代碼注解。

zend_hash_del_key_or_index

函數執行步驟

  • 計算key的哈希值和下標
  • 遍歷哈希值所在的bucket,如果找到key所在的bucket,則進行第三步,否則,指向下一個bucket,直到指向bucket鏈表中的最后一個位置
  • 如果要刪除的是第一個元素,直接將arBucket[nIndex]指向第二個元素;其余的操作是將當前指針的last的next執行當前的next
  • 調整相關指針
  • 釋放數據內存和bucket結構體內存

詳細代碼和注解請點擊:zend_hash_del_key_or_index代碼注解。

性能分析

PHP的哈希表的優點:PHP的HashTable為數組的操作提供了很大的方便,無論是數組的創建和新增元素或刪除元素等操作,哈希表都提供了很好的性能,但其不足在數據量大的時候比較明顯,從時間復雜度和空間復雜度看看其不足。

不足如下:

  • 保存數據的結構體zval需要單獨分配內存,需要管理這個額外的內存,每個zval占用了16bytes的內存;
  • 在新增bucket時,bucket也是額外分配,也需要16bytes的內存;
  • 為了能進行順序遍歷,使用雙向鏈表連接整個HashTable,多出了很多的指針,每個指針也要16bytes的內存;
  • 在遍歷時,如果元素位于bucket鏈表的尾部,也需要遍歷完整個bucket鏈表才能找到所要查找的值

PHP的HashTable的不足主要是其雙向鏈表多出的指針及zval和bucket需要額外分配內存,因此導致占用了很多內存空間及查找時多出了不少時間的消耗。

后續

上面提到的不足,在PHP7中都很好地解決了,PHP7對內核中的數據結構做了一個大改造,使得PHP的效率高了很多,因此,推薦PHP開發者都將開發和部署版本更新吧。看看下面這段PHP代碼:

?php
$size = pow(2, 16); 
 
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = ($size - 1) * $size; $key = $maxKey; $key += $size) {
  $array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 個惡意的元素需要 ', $endTime - $startTime, ' 秒', "\n";
 
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = $size - 1; $key = $maxKey; ++$key) {
  $array[$key] = 0;
}
$endTime = microtime(true);
echo '插入 ', $size, ' 個普通元素需要 ', $endTime - $startTime, ' 秒', "\n";

上面這個demo是有多個hash沖突時和無沖突時的時間消耗比較。筆者在PHP5.4下運行這段代碼,結果如下

插入 65536 個惡意的元素需要 43.72204709053 秒

插入 65536 個普通元素需要 0.009843111038208 秒

而在PHP7上運行的結果:

插入 65536 個惡意的元素需要 4.4028408527374 秒

插入 65536 個普通元素需要 0.0018510818481445 秒

可見不論在有沖突和無沖突的數組操作,PHP7的性能都提升了不少,當然,有沖突的性能提升更為明顯。至于為什么PHP7的性能提高了這么多,值得繼續深究。

最后再安利一下,筆者github上有一個簡易版的HashTable的實現:HashTable實現

另外,我在github有對PHP源碼更詳細的注解。感興趣的可以圍觀一下,給個star。PHP5.4源碼注解。可以通過commit記錄查看已添加的注解。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • PHP實現的服務器一致性hash分布算法示例
  • PHP實現的一致性哈希算法完整實例
  • PHP內核探索:哈希表碰撞攻擊原理
  • PHP中創建和驗證哈希的簡單方法實探
  • php內核解析:PHP中的哈希表
  • php-perl哈希算法實現(times33哈希算法)
  • PHP 5.5 創建和驗證哈希最簡單的方法詳解
  • 一致性哈希算法以及其PHP實現詳細解析
  • 如何用PHP實現分布算法之一致性哈希算法

標簽:哈密 池州 阿里 孝感 那曲 濟源 北京 日照

巨人網絡通訊聲明:本文標題《PHP哈希表實現算法原理解析》,本文關鍵詞  PHP,哈希,表,實現,算法,原理,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《PHP哈希表實現算法原理解析》相關的同類信息!
  • 本頁收集關于PHP哈希表實現算法原理解析的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产欧美一区二区三区在线看蜜臀| 久久新电视剧免费观看| 国产三区在线成人av| 成人性视频免费网站| 久久久www免费人成精品| caoporn国产一区二区| 亚洲欧美色综合| 欧美精品乱码久久久久久| 欧美aaa在线| 中文字幕av资源一区| 欧美自拍丝袜亚洲| 久久爱www久久做| 国产精品国产三级国产三级人妇 | 国产日韩欧美制服另类| 国产性天天综合网| 最新久久zyz资源站| 91啦中文在线观看| 蜜臂av日日欢夜夜爽一区| 中文字幕av一区二区三区高| 国产欧美一区二区三区鸳鸯浴 | 亚洲成人免费影院| 欧美精品一区视频| 色欧美乱欧美15图片| 蜜桃久久av一区| 国产美女主播视频一区| 亚洲综合成人网| 欧美国产精品久久| 亚洲综合av网| 国产精品18久久久久久久久 | 成人在线视频一区| 日本国产一区二区| 91网站最新网址| 欧美一区二区三区小说| 色哟哟精品一区| 欧美不卡一区二区三区四区| 欧美精品乱人伦久久久久久| 国产日韩欧美综合一区| 亚洲大片在线观看| 粉嫩aⅴ一区二区三区四区| 麻豆91免费看| 色94色欧美sute亚洲线路二| 精品国产亚洲在线| 午夜精品久久久久久久久| 一区二区三区色| 一二三区精品视频| 国产99久久久国产精品| 日韩一区二区免费电影| 欧美日韩不卡一区| 欧美系列一区二区| 91精品国产麻豆国产自产在线 | 国产清纯白嫩初高生在线观看91| 一区二区三区美女| 国产成人一区在线| gogogo免费视频观看亚洲一| 日韩视频免费观看高清完整版 | 午夜日韩在线电影| 97久久久精品综合88久久| av不卡免费在线观看| 日韩精品专区在线| 国产视频一区在线播放| 午夜精品久久久久久不卡8050| 高清beeg欧美| 久久久久久电影| 另类小说一区二区三区| 8x8x8国产精品| 欧美精品一区二区高清在线观看| 亚洲不卡在线观看| 欧美日韩一区二区三区高清 | 国产在线一区二区综合免费视频| 国产精品资源网站| 26uuu色噜噜精品一区| 免费在线观看成人| 欧美一级在线视频| 久久激情五月婷婷| 精品sm在线观看| 国产91丝袜在线播放0| 久久久久久亚洲综合| 麻豆专区一区二区三区四区五区| 91精品国产免费| 激情综合网天天干| 色狠狠综合天天综合综合| 综合久久综合久久| 色欧美日韩亚洲| 亚洲国产乱码最新视频 | 91高清视频免费看| 亚洲综合色网站| 欧美日韩情趣电影| 美女mm1313爽爽久久久蜜臀| 日韩一区二区三区免费看| 美女国产一区二区三区| 久久精品一二三| 99久久久久久| 无吗不卡中文字幕| 久久女同精品一区二区| 成人国产精品免费观看动漫| 欧美一区二区在线观看| 精品一区二区三区免费毛片爱| www成人在线观看| 成人精品视频.| 午夜电影一区二区三区| 精品久久久久久久久久久院品网| 国产成人精品一区二区三区四区| 亚洲精品国产高清久久伦理二区| 国产自产视频一区二区三区| 国产精品国产精品国产专区不蜜| 91官网在线免费观看| 美国欧美日韩国产在线播放| 国产精品视频线看| 国产一区二区三区四区五区美女| 国产精品成人免费在线| 91精品久久久久久久99蜜桃 | 欧美三电影在线| 国产精品一区在线观看你懂的| 17c精品麻豆一区二区免费| 91精品国产欧美日韩| 成人精品高清在线| 久久99精品久久只有精品| 亚洲男人的天堂一区二区| 久久久久久久久97黄色工厂| 在线观看亚洲成人| 成人黄页在线观看| 狠狠色丁香婷婷综合久久片| 一区二区三区国产精品| 国产日本亚洲高清| 26uuu久久综合| 3d动漫精品啪啪一区二区竹菊 | 国产大片一区二区| 另类小说一区二区三区| 亚洲国产成人tv| 亚洲天堂成人在线观看| 久久亚洲综合色一区二区三区| 欧美三级视频在线| 91色视频在线| aaa欧美日韩| 不卡电影一区二区三区| 国产激情视频一区二区在线观看| 日韩二区三区四区| 日韩欧美另类在线| 3d成人h动漫网站入口| 日本精品免费观看高清观看| 国产成人免费在线观看不卡| 久久国产视频网| 精品一区二区三区免费| 日本欧美加勒比视频| 亚洲国产精品自拍| 亚洲一区二区四区蜜桃| 亚洲主播在线观看| 亚洲地区一二三色| 日韩国产欧美三级| 另类综合日韩欧美亚洲| 老司机精品视频一区二区三区| 蜜桃久久久久久| 狠狠色丁香久久婷婷综| 国产精品一级在线| 国产成人精品综合在线观看| 国产91精品免费| 99精品欧美一区二区蜜桃免费 | 日韩专区在线视频| 国产人妖乱国产精品人妖| 久久久精品人体av艺术| 国产精品久久久久aaaa樱花| 国产精品福利一区| 一区2区3区在线看| 蜜桃视频免费观看一区| 激情综合亚洲精品| 99久久精品一区| 欧美福利视频导航| 欧美xfplay| 国产精品久久网站| 亚洲一区免费观看| 麻豆一区二区在线| 成人精品视频一区二区三区| 欧美在线视频不卡| 欧美一区二区三区免费视频| 久久久国产午夜精品| 中文字幕欧美一| 免费美女久久99| 色综合久久综合网欧美综合网| 欧美视频一二三区| 久久一夜天堂av一区二区三区| 最新国产の精品合集bt伙计| 五月天欧美精品| 粉嫩高潮美女一区二区三区| 欧美在线一区二区| 日本一区二区免费在线| 亚洲男同性视频| 国产福利一区二区三区视频| 欧洲一区二区av| 国产日韩高清在线| 日本va欧美va瓶| 色婷婷激情一区二区三区| 欧美一级艳片视频免费观看| 亚洲欧美电影一区二区| 久久66热偷产精品| 欧美午夜片在线观看| 国产日韩一级二级三级| 全国精品久久少妇| 欧美亚洲图片小说| 国产精品久久久久久久久久免费看 | 欧美精品在线观看一区二区|