剖析PHP底層陣列是如何實現的

2021-09-20 21:19:44 字數 3408 閱讀 4475

php是一門入門容易,使用範圍廣泛的語言,以其靈活性以及web後端開發被很多人熟知,也被很多人戲稱「php是世界上最好的語言」。本人是一名「忠實」的phper,相信用過php的程式設計師都會體會到php陣列的靈活性,相對傳統的c語言,使用起來很是方便,擁有關聯陣列(key值可以是字串),不需要預定義陣列空間大小,關聯陣列,不需要指定key的快速索引賦值等等便利方法,這段時間研究了一下php陣列的底層結構,並總結分析,裡面含有一些我自己的猜想,如有錯誤請指出。
雜湊結構是一種非常重要的資料結構,他是一種通過key對映到value的結構,由於其特性,可以在大部分的情況下讓查詢和插入的效率達到o(1),在很多語言或者系統裡面都有顯性得體現出來,具體的實現思路有很多種。詳細的介紹可以看我的部落格資料結構之雜湊結構

php的陣列是用鏈位址法的雜湊結構去實現的,鍊錶是雙向鍊錶,這樣既可以動態分配陣列空間,也可以通過key值去計算hash值去訪問對應的元素,是一種非常高效的資料結構。

下面是php  bucket的結構,bucket是乙個基本結點的結構,bucket是以存放基本元素的容器,可以簡單理解為陣列元素的房子。

typedef struct bucketbucket;

const char * pdata; //模擬記錄php資料,原來是void *pdata和 void *pdataptr

}bucket;

下面是php  hashtable結構,hashtable是用以儲存bucket陣列和bucket資訊的雜湊表結構,採用雙向鍊錶的拉鍊法結構。

typedef struct hashtable

}

可能首次去看資料結構可能會覺得有點難受,密密麻麻的一堆東西,下面我會乙個個分析資料字段。

1.h(雜湊值)

通過key對映的雜湊值(未經過糾正)h,為了讓不同key值均勻分配到雜湊表的各個位置,必須要有乙個好的雜湊函式,而php選用的是time33演算法,也就是下面的演算法(簡化版)。

ulong hash(const char* key)

return hash;

}

當然啦在php的具體實現細節又會有點不同,但是原理是差不多的。

2.nkeylength(字元的個數)

如果使用的是關聯索引,那麼此處nkeylength就是字元的個數,比如說$arr['key']='value' ,那麼這個值就為3,如果是索引陣列,此字段就不會用上。

3.pnext plast (記錄該桶的前後桶)

4.plistnext(記錄該桶在資料上的後元素)

這個欄位從命名意思就可以看出,是鍊錶的指向後繼元素的指標。比如說作如下賦值。guangdong的plistnext指向beijing,beijing的plistnext指向shanghai.....

$arr[2]="guangdong";

$arr[1]="beijing";

$arr[3]="shanghai";

$arr[4]="zhejiang";

所以你如果用foreach去遍歷陣列,會發現乙個很有趣的現象。輸出的結果如下,居然不是按照陣列下標1,2,3,4順序去輸出,其實只要你理解了php的儲存陣列的資料結構你就很明白了。

2 => "guangdong"

1 => "beijing"

3 => "shanghai"

4 => "zhejiang"

他的資料結構如下圖顯示,第乙個元素是guangdong,然後來個元素beijing,於是guangdong的plistlast指向beijing,後面的元素同理。而foreach遍歷會從第乙個元素(也就是plisthead指向的bucket,詳看下文hashtable的介紹)去輸出,然後再指向下乙個元素,因此輸出的順序不是按照下標來的,而是按照賦值順序來的,這也是為什麼foreach遍歷陣列要比for遍歷要快的原因,因為for每次查詢元素都要去做一次雜湊對映查詢對應下標的bucket,而foreach只需要遍歷bucket鍊錶就好了。plistlast與plistnext同理,只是指向前乙個陣列元素。

5.arkey(用以儲存key值)

1.ntablesize(雜湊表的大小)

這是雜湊表的分配bucket空間的大小,缺省會分配8個bucket空間,當儲存元素個數大於8個就會儲存16個,如此下去,儲存的個數為2x大小,即8,16,32,64...

2.ntablemask(糾正掩碼)

用以糾正過長的雜湊值,值為ntablesize-1,比如說乙個我有乙個字元經過雜湊函式得出值為9,但是ntablesize為8,那該怎麼辦呢,存放到第1個位置吧,計算方法就是9   mod 8,但是在計算機裡面下標是從0開始,因此我們會使用&運算得出結果,9&7=1。

3.nnumofelements(陣列元素個數)

用以統計陣列元素的個數,php的count()元素其實就是獲取這個值。

4.nextfreeelement(下乙個空閒的元素)

用以儲存下乙個空閒的元素的值。當你的陣列是索引陣列,用到$arr=value賦值就會用到,如果你上次賦值的元素下標是100,那麼nextfreeelement就為101了。無關你的元素個數。

5.plisthead(鍊錶的頭部元素)

這個bucket指標從名字就可以看出來,用以指向鍊錶的頭部元素,例如你給乙個陣列第一次附上乙個值$arr=value1,那麼這個指標就是指向value1。

6.plisttail(鍊錶的尾部元素)

原理同上,只是指向尾部元素,每次來乙個新的陣列元素,plisttail就會指向它。

7.ninternalpointer(用以指向內部指向的元素)

如果我們用foreach遍歷陣列,這個指標就會指向當前遍歷的元素,用以儲存當前指向記錄。用到此項的還有current(),next(),prev()函式。

8.arbuckets(用以儲存bucket在c的內部陣列)

此項為指標的指標,可以用於操作bucket陣列。

下面列出一副圖來說明php的陣列結構(為版面清晰忽略了兩種指向前乙個bucket的指標:plistlast,plast)。

最後我大概猜想一下foreach函式的執行過程,首先是將ninternalpointer指向hashtable的第乙個buckets,也就是plisthead,如果不為空則輸出該元素,然後ninternapointer指向該bucket的下乙個元素,也就是plistnext,如此迴圈下去。

void foreach_print(hashtable *ht)

}

剖析PHP底層陣列是如何實現的

php是一門入門容易,使用範圍廣泛的語言,以其靈活性以及web後端開發被很多人熟知,也被很多人戲稱 php是世界上最好的語言 本人是一名 忠實 的phper,相信用過php的程式設計師都會體會到php陣列的靈活性,相對傳統的c語言,使用起來很是方便,擁有關聯陣列 key值可以是字串 不需要預定義陣列...

棧的底層(陣列)實現和棧實現的綜合計算器

使用棧完成計算乙個表示式的結果 10 9 3 5 public class usestack public static int calculate string str else else else else index if index expression.length while opers...

STL實現的底層資料結構簡介

c stl 的實現 1.vector 底層資料結構為陣列 支援快速隨機訪問 2.list 底層資料結構為雙向鍊錶,支援快速增刪 3.deque 底層資料結構為乙個 控制器和多個緩衝區,詳細見stl原始碼剖析p146,支援首尾 中間不能 快速增刪,也支援隨機訪問 4.stack 底層一般用23實現,封...