php陣列實現原理

2020-07-16 10:05:42

陣列是PHPer最常用的資料型別,同時php容易上手也得益於其強大的陣列,但是陣列在php中是如何實現的呢?

推薦教學:PHP視訊教學

首先,我們還是先了解下相關的資料結構,為下面的內容打好基礎

雜湊表

  雜湊表,顧名思義,即將不同的關鍵字對映到不同單元的一種資料結構。而將不同關鍵字對映到不同單元的方法就叫做雜湊函數

  理想情況下,經過雜湊函數處理,關鍵字和單元是會進行一一對應的;但是如果關鍵字值足夠多的情況下,就容易出現多個關鍵字對映到同一單元的情況,即出現雜湊衝突

  雜湊衝突的解決方案,要麼使用連結法,要麼使用開放定址法

連結法
  即當不同的關鍵字對映到同一單元時,在同一單元內使用連結串列來儲存這些關鍵字

開放定址法
  即當插入資料時,如果發現關鍵字被對映到的單元存在資料了,說明發生了衝突,就繼續尋找下一個單元,直到找到可用單元為止

  而因為開放定址法方案屬於佔用其他關鍵字對映單元的位置,所以後續的關鍵字更容易出現雜湊衝突,因此容易出現效能下降

連結串列

  既然上面提到了連結串列,這裡我們簡單聊一下連結串列的基礎知識。連結串列分為很多種型別,常用的資料結構包括:佇列,棧,雙向連結串列等

  連結串列,就是由不同的連結串列節點組成的一種資料結構。連結串列節點一般由元素+指向下一節點的指標組成。而雙向連結串列,顧名思義,則是由指向上一節點的指標+元素+指向下一節點的指標組成

  對於資料結構的內容,我們不過多展開,我們之後會有專門的內容去詳細介紹資料結構

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的變數容器。

雜湊表內部結構關係圖

php41.png

  從上圖我們可以看出,Bucket在存放資料的時候,如果存在雜湊衝突,則將多個關鍵字對映到連結串列中,由此組成了雙向連結串列

總結

  今天,我們以陣列作為切入點,簡單了解了下基本的資料結構:雜湊表和連結串列;並且了解了陣列的底層實現,即雜湊表+雙向連結串列。其實雜湊表作為php中最重要的資料結構,用處很廣。變數的符號表,函數列表等都是用雜湊表來儲存的

以上就是php陣列實現原理的詳細內容,更多請關注TW511.COM其它相關文章!