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實現,封...