本文來自《Lua設計與實現》的閱讀筆記,推薦Lua學習者能夠購買一本,深刻淺出講解lua的設計和實現原理,很贊,哈哈
Lua中對於表的設計,是基於數組和散列表,和其餘語言不一樣,對於數組的下標是從1開始的,對於散列表而言,只要其鍵值補位nil,均可以存儲在其中。
首先看看table的數據定義,參考源碼lobject.h
flags 這是一個lua的byte類型的數據,用於表示表中提供了哪些元方法,好比是否提供了元方法_index,該數據最開始設置爲1,若是進行查找一次,好比_index,若是存在,這該元方法對應的flag bit設置爲0,在下一次查找的時候,只須要比較這個bit便可,對應的元方法在ltm.h中
lsizenode,爲散列表的大小,一定爲2的冪對應的數字;
node, 該table的散列表的起始位置的指針;
sizearray, 數組的大小,不必定爲2的冪對應的數字
對於node數據,相似於其餘語言中的字典設計\hash設計,就是一個鍵值對集合,其定義爲:
須要提一下的是對於key的設計採用的是union,也就是說Lua的散列表的key,能夠爲nk對應的struct,也能夠是TValue類型
if 輸入的key爲整數 && key >= 0 && key <= 數組的大小
計算出該key的散列值,據其查找對應的node所在散列表中的位置,而後遍歷其對應的鏈表,查找是否有該key對應的元素
那麼1是在數組中查找,100就是在散列表中去查找了(100大於數組的len)
給lua中添加新元素的時候,會有可能觸發從新分配table中的數組和散列表,其本質來自於散列表的rehash(因爲lua對於下標超過數組的大小的數字,都會存儲在散列表部分去,因此數組部分的插值不會觸發rehash)
散列表的組織,就是多個mainposition,每一個單獨的mainposition會對應一個數據鏈表,當插入一個key的時候,會調用luaHset\luaH_setnum\luaH_setstr,來得到該key對應的TValue指針,若是沒有,則調用內部的newkey函數來分配一個新的key:
基本的實現過程看源代碼寫的比較詳細,這兒說一下rehash部分的操做,在ltable.c中:
2)分表遍歷數組(numusearray)和散列表(numusehash),統計更新nums中的數量大小
3) 從新計算數組和hash部分的大小,數組大小的計算規則:逐個遍歷nums數組,得到其範圍區間內所包含的整數數量大於50%的最大索引,做爲rehash後的數組大小,這個索引值來自與computesizes函數:
首先nums數組在統計後,每一個下標對應的是處於當前2^(i -1) - 2^i中的元素的個數,而後不斷的累加計算,求得知足 sum > 2^n/2的最大下標值(這個下標值是nums數組中的)
因此,在不一樣的rehash階段,table中的同一個key可能會在數組部分和散列表部分交替出現,也是可能的。
因爲rehash會帶來較大的性能消耗,因此通常都儘可能避免,好比在建立表的時候,就採用預填充的算法
若是table中元表沒有重載len方法,則調用的是luaH_getn方法,其基本的僞代碼爲:
對於表中只有散列表的時候,其實質就是對鍵值爲正整數的部分進行長度操做,若是既有數組,又有散列表,則優先對數組部分進行長度操做