php底層原理之數組實現

數組是PHPer最經常使用的數據類型,同時php容易上手也得益於其強大的數組,可是數組在php中是如何實現的呢?php

首先,咱們仍是先了解下相關的數據結構,爲下面的內容打好基礎segmentfault

哈希表

哈希表,顧名思義,即將不一樣的關鍵字映射到不一樣單元的一種數據結構。而將不一樣關鍵字映射到不一樣單元的方法就叫作哈希函數數組

理想狀況下,通過哈希函數處理,關鍵字和單元是會進行一一對應的;可是若是關鍵字值足夠多的狀況下,就容易出現多個關鍵字映射到同一單元的狀況,即出現哈希衝突網絡

哈希衝突的解決方案,要麼使用連接法,要麼使用開放尋址法數據結構

連接法
即當不一樣的關鍵字映射到同一單元時,在同一單元內使用鏈表來保存這些關鍵字函數

開放尋址法
即當插入數據時,若是發現關鍵字被映射到的單元存在數據了,說明發生了衝突,就繼續尋找下一個單元,直到找到可用單元爲止性能

而由於開放尋址法方案屬於佔用其餘關鍵字映射單元的位置,因此後續的關鍵字更容易出現哈希衝突,所以容易出現性能降低優化

鏈表

既然上面提到了鏈表,這裏咱們簡單聊一下鏈表的基礎知識。鏈表分爲不少種類型,經常使用的數據結構包括:隊列,棧,雙向鏈表等ui

鏈表,就是由不一樣的鏈表節點組成的一種數據結構。鏈表節點通常由元素+指向下一節點的指針組成。而雙向鏈表,顧名思義,則是由指向上一節點的指針+元素+指向下一節點的指針組成spa

對於數據結構的內容,咱們不過多展開,咱們以後會有專門的內容去詳細介紹數據結構

php數組

php解決哈希衝突的方式是使用了連接法,因此php數組是由哈希表+鏈表實現,準確來講,是由哈希表+雙向鏈表實現

內部結構-哈希表

HashTable結構體主要用來存放哈希表的基本信息

typedef struct _hashtable { 
    uint nTableSize;        // hash Bucket的大小,即哈希表的容量,最小爲8,以2x增加。
    uint nTableMask;        // nTableSize-1 , 索引取值的優化
    uint nNumOfElements;    // hash Bucket中當前存在的元素個數,count()函數會直接返回此值 
    ulong nNextFreeElement; // 下一個可以使用的數字鍵值
    Bucket *pInternalPointer;   // 當前遍歷的指針(foreach比for快的緣由之一)
    Bucket *pListHead;          // 存儲整個哈希表的頭元素指針
    Bucket *pListTail;          // 存儲整個哈希表的尾元素指針
    Bucket **arBuckets;         // 存儲hash數組
    dtor_func_t pDestructor;    // 在刪除元素時執行的回調函數,用於資源的釋放
    zend_bool persistent;       //指出了Bucket內存分配的方式。若是persisient爲TRUE,則使用操做系統自己的內存分配函數爲Bucket分配內存,不然使用PHP的內存分配函數。
    unsigned char nApplyCount; // 標記當前hash Bucket被遞歸訪問的次數(防止屢次遞歸)
    zend_bool bApplyProtection;// 標記當前hash桶容許不容許屢次訪問,不容許時,最多隻能遞歸3次
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;

Bucket結構體則用於保存數據的具體內容

typedef struct bucket {
    ulong h;            // 對char *key進行hash後的值,或者是用戶指定的數字索引值
    uint nKeyLength;    // hash關鍵字的長度,若是數組索引爲數字,此值爲0
    void *pData;        // 指向value,通常是用戶數據的副本,若是是指針數據,則指向pDataPtr
    void *pDataPtr;     // 若是是指針數據,此值會指向真正的value,同時上面pData會指向此值
    struct bucket *pListNext;   // 指向整個哈希表的該單元的下一個元素
    struct bucket *pListLast;   // 指向整個哈希表的該單元的上一個元素
    struct bucket *pNext;       // 指向因爲哈希衝突致使存放在同一個單元的鏈表中的下一個元素
    struct bucket *pLast;       // 指向因爲哈希衝突致使存放在同一個單元的鏈表中的上一個元素
    // 保存當前值所對於的key字符串,這個字段只能定義在最後,實現變長結構體
    char arKey[1];              
} Bucket;

其中Bucket結構體內有指向用戶數據的pData元素,實際上是指向了以前咱們介紹的變量zval結構體,這也是爲何當建立數組時,會出現數組元素+1的變量容器。不瞭解變量底層相關知識的,請查看我以前的文章:

php底層原理之變量(一)
php底層原理之變量(二)

哈希表內部結構關係圖

哈希表內部結構關係圖
注:圖片來源於網絡

從上圖咱們能夠看出,Bucket在存放數據的時候,若是存在哈希衝突,則將多個關鍵字映射到鏈表中,由此組成了雙向鏈表

總結

今天,咱們以數組做爲切入點,簡單瞭解了下基本的數據結構:哈希表和鏈表;而且瞭解了數組的底層實現,即哈希表+雙向鏈表。其實哈希表做爲php中最重要的數據結構,用處很廣。變量的符號表,函數列表等都是用哈希表來存儲的,感興趣的同窗能夠看我以前的文章來了解相關知識

相關文章
相關標籤/搜索