PHP關聯陣列與雜湊表(hash table) 不指定

來源:互聯網
上載者:User

PHP中有一種資料類型非常重要,它就是關聯陣列,又稱為雜湊表(hash table),是一種非常好用的資料結構。

在程式中,我們可能會遇到需要消重的問題,舉一個最簡單的模型:

有一份使用者名稱列表,儲存了 10000 個使用者名稱,沒有重複項;
還有一份黑名單列表,儲存了 2000 個使用者名稱,格式與使用者名稱列表相同;
現在需要從使用者名稱列表中刪除處在黑名單裡的使用者名稱,要求用盡量快的時間處理。

這個問題是一個小規模的處理量,如果實際一點,2 個表都可能很大,比如有 2 億條記錄。

我最開始想到的方法,就是做一個嵌套的迴圈,設使用者名稱表有 M 條記錄,黑名單列表有 N 條記錄,那麼,迴圈的次數是 M * N 次!
PHP 版代碼:


01 <?php  
02 foreach($arrayM as $keyM => $nameM) {  
03 foreach($arrayN as $nameN) {  
04 if ($nameM == $nameN) {  
05 // 本行執行了 M * N 次!  
06 unset($arrayM[$keyM]);  
07 }  
08 }  
09 }  
10 return $arrayM;  
11 ?>
另一種方式,利用數組索引。

PHP 是一種弱類型的語言,不像 C 語言那樣有嚴格的變數類型限制。C 語言的數組,每一個元素的類型必須一致,而且索引都是從 0 開始。
PHP 的數組,可以用字串作為索引,也稱為關聯陣列。
數組索引,有一個天然的限制就是不會重複,而且訪問的時候不需要尋找,可以直接定位。

還是剛才的那個問題,我們採用另一種辦法。

把黑名單列表的使用者名稱組織到一個數組裡,數組的索引就是使用者名稱。

然後,遍曆使用者列表的時候,只需直接用 isset 查詢那個使用者名稱是否存在即可。

PHP 版代碼:


01 <?php  
02 $arrayHash = array();  
03 foreach($arrayN as $nameN) {  
04 // 本行執行了 N 次。  
05 $arrayHash[$nameN] = 1;  
06 }  
07  
08 foreach($arrayM as $keyM => $nameM) {  
09 if (isset($arrayHash[$nameM])) {  
10 // 本行執行了 M 次!  
11 unset($arrayM[$keyM]);  
12 }  
13 }  
14 return $arrayM;  
15 ?>
可以看到,最佳化過的代碼,迴圈次數是 M + N 次。

假如 M 和 N 都是 10000,最佳化前,迴圈了 1 億次;最佳化後,只迴圈了 20000 次,差了 5000 倍!
如果第二個程式耗時 1 秒,則第一個程式需要將近一個半小時!

=========================================================================
hash一個貌似比較複雜的東西,實際上理解起來並不那麼誇張,這裡做個筆記。

hash,中文翻譯成雜亂的東西,有人也叫它雜湊,或者翻譯成什麼都不是的音譯“雜湊”。

簡單說來,hash就是為了把一個複雜的字串,通過一定的轉換,得到一個簡單的數字(通常是數字)。
如"abcd" 用各個字元的值直接相加,再取對10的餘數,既(a+b+c+d),來得到一個數字,比方說結果為5,那麼這個5就能在一定意義上代表這個字串 abcd了。或者說這個5也可以說是這個字串的一個標記性的東西,而且是簡化了的標記,所以又有人叫這個5為字串的摘要,或指紋。
這個5,有一個好的用處就是可以作為一個數組的下標來用,如我自己構造一個指標數組void* hash_array[10],那麼我就可以把5那個位置上填上一個指標,如指向abcd字串。
這樣的話,我如果要去查詢一個字串是否存在,就不需要對一個數組使用字串迴圈對比這樣的慢操作,而直接先得到某個字串的hash值,再用這個hash值,在數組下標裡直接找,這樣速度要快上很多,特別是資料比較多的時候。

可以看到上面計算hash值時,出來的結果,可能並不是從0開始的,如我們算出的就是5。也就是說,這個5是在數組中的某個不確定的位置,或者可以叫做是一個雜湊出來的位置。其他位置可能一直就空著在。這就是這個數組或表格叫hash表的原因了。

但有個問題,上面的轉換方法,直接相加,再取個餘數,在字串變為abdc時,結果得到的還是數字5。這個就是上面這個演算法的一個問題了,即它不能保證一個唯一性。所以就出現了很多hash演算法的研究,如MD4,MD5,SHA-1等,來保證唯一性。
但上面這個演算法還是可以使用的,做法就是在abdc經過hash得到5後,去檢查5是否被佔用,如果佔用了,那麼就把數字加1,即為6,如果6沒被佔用,就填上值。如果後面某個字串算出一個值是6,但6已經被佔用了,那麼就再加1,再存。
取資料的時候,可以先算出hash值後,再看裡面的內容是不是你想要的,如果不是,就加1去看,最後得到一個。

所以這裡hash表的內容並不是象一般的數組最開始就組織好了的,而是後續慢慢往裡增加的。
hash表裡存的內容一般可以是一個指標,這個指標可以指向一個大的結構也是可以的。這個結構裡可以有key, value資訊。
hash表也可以不是數組,你可以把它組織成一個鏈表,鏈表裡的node的結構中可以有一個參數就是那個數位hash_value,用來快速尋找用。

雖然在很多時候hash被用在加密等場合,但在一般的應用程式代碼中,也可以用它來存貯簡單的資料,這樣代碼的效率會高很多。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.