深析PHP陣列是怎麼靈活支援多資料型別

2023-03-25 18:01:01

本篇文章給大家帶來了關於PHP的相關知識,其中主要跟大家介紹陣列是怎麼靈活支援多資料型別的,感興趣的朋友下面一起來看一下吧,希望對大家有幫助。

在PHP中,陣列資料結構的應用處理是使用頻率非常高的,相對於Java、C++ 這種強型別語言來說,PHP的陣列簡直可以說是太好用了,可以儲存各種型別的資料(如:數位、字串甚至物件等),為開發帶來了極大的便利。

基於 PHP 陣列的強大特性,我們可以輕易實現更加複雜的資料結構,比如棧、佇列、列表、集合、字典等。

c900c28ca9f01164d6fc72c4aa14f68.png

你是否迫不及待的想要一探究竟:PHP到底是如何實現陣列的呢?

1、PHP陣列底層資料結構

PHP 陣列其內部是使用 HashTable 結構來實現的,那就先來簡單說說HashTable吧!

HashTable又稱雜湊表,是通過key-value的方式來高效地存取資料的一種結構。雜湊表是陣列和連結串列的一種合併,整合了陣列的定址快,連結串列的插入快的特點於一身。

807ec9519dc0f8a1a02633f9be839cb.png

HashTable主要分為兩個環節:

1. 雜湊函數:雜湊函數將要查詢的值轉換成數位索引,通過數位索引可以快速的找到值存在的位置。

2. 雜湊碰撞:理想情況下,不同的值通過雜湊函數後,出來的結果是不一樣的;如果不一樣的值,雜湊後出來一樣的數位,我們稱之為雜湊碰撞。

因此應用 HashTable 就必須要面臨解決雜湊碰撞的問題,主要的解法有兩種:連結串列法,開放定址法。

在zend_type.h檔案中,可以找到 HashTable 的主要結構定義如下:

zend_陣列 型別

挑選幾個重點成員介紹一下:

  • gc: 參照計數,垃圾回收使用。

  • arData:雜湊表中儲儲存存元素的陣列,其記憶體是連續的,arData指向陣列的起始位置;

  • nTableSize:陣列的總容量,即可以容納的元素數,arData 的記憶體大小就是根據這個值確定的,它的大小的是2的冪次方,最小為8,然後按照 8、16、32...依次遞增;

2dfd4156fea7e822dca10caf95f12ab.png

Bucket 型別

Bucket 的結構比較簡單,主要用來儲存元素的 key 和 value,以及一個整型的 h(雜湊值,或者叫雜湊值)。

  • 如果元素是數值索引,則其值就是數值索引的值;

  • 如果是字串索引,那麼其值就是 key 通過 Time33 演演算法計算得到的雜湊值。

h 的值用來最終對映元素的儲存位置。

2、PHP 陣列的基本實現

上面部分我們瞭解了 zend_陣列 的資料結構,那接著看看陣列的初始化吧:

陣列的初始化主要是針對 HashTable 成員的設定,初始化時並不會立即分配 arData 的記憶體,插入第一個元素之後才會分配 arData 的記憶體。

為了更好的理解整個hash結構,我們來舉個例子說明一下這個結構:

$data = array(
    'hello' => 'haha',
    1       => 'me to'
    'world' => 'world', 
    2       => 2
);
unset($data[1]);
登入後複製

那上面的hash結構應該是什麼樣的呢?arData儲存的結果應該是什麼樣呢?

畫個圖例來看看吧,更直觀一些:

arData是Bucket型別的指標,用來具體儲存每個元素的key,value,按照插入元素的順序儲存資料的,所以陣列的順序也是靠這個來保證。

每個arData陣列的元素,從圖中可以看到,左邊負數是雜湊值取模後的值,儲存的是右邊arData的索引;如-8衝突了,則儲存了連結串列的頭元素。

arData[0]: key='hello',h=xx(具體某個值),val = 'haha'

arData[1]: val是 type=IS_UNDEF 的zval(被unset後,不是立即被刪除,而是置成IS_UNDEF)

arData[2]: key='world',h=xx(具體某個值),val = 'world'

arData[3]: key=NULL,h=2(可能會雜湊值衝突),val = 2

….

上面的例子很具體地解釋了nNumUsed,nNumOfElements,arData的意義。

3、PHP 陣列的有序性

陣列中各元素的順序和插入順序一致,這個是怎麼實現的呢?

為了實現 PHP 陣列的有序性,PHP 底層的雜湊表在雜湊函數與元素陣列之間加了一層對映表,這個對映表也是一個陣列,大小和儲存元素的陣列相同,儲存元素的型別為整型,用於儲存元素在實際儲存的有序陣列中的下標 —— 元素按照先後順序依次插入實際儲存陣列,然後將其陣列下標按照雜湊函數雜湊出來的位置儲存在新加的對映表中:

這樣,就可以完成最終儲存資料的有序性了。

PHP 陣列底層結構中並沒有顯式標識這個中間對映表,而是與 arData 放到了一起,在陣列初始化的時候並不僅僅分配用於儲存 Bucket 的記憶體,還會分配相同數量的 uint32_t 大小的空間,這兩塊空間是一起分配的,然後將 arData 偏移到儲存元素陣列的位置,而這個中間對映表就可以通過 arData 向前存取到。

總結

PHP中的陣列其特點就是將 values 對映到 keys 的型別。與其他語言不同的是,PHP中陣列的 key 可以是字串,而values可以是任意型別。

除常規增刪改查之外,陣列還有很多其他操作,比如複製、合併、銷燬、重置等,這些操作對應的程式碼都位於 zend_hash.c 中,感興趣的同學可以去了解一下。

推薦學習:《》

以上就是深析PHP陣列是怎麼靈活支援多資料型別的詳細內容,更多請關注TW511.COM其它相關文章!