openresty開發系列19--lua的table操作
Lua中table內部實際采用哈希表和數組分別保存鍵值對、普通值;下標從1開始
不推薦混合使用這兩種賦值方式。
local color={first="red", "blue", third="green", "yellow"}
print(color["first"]) --> output: red
print(color[1]) --> output: blue
print(color["third"]) --> output: green
print(color[2]) --> output: yellow
print(color[3]) --> output: nil
一)table.getn 獲取長度
相關於取長度操作符寫作一元操作 #。
字符串的長度是它的字節數(就是以一個字符一個字節計算的字符串長度)。
對於常規的數組,里面從 1 到 n 放着一些非空的值的時候,它的長度就精確的為 n,即最后一個值的下標。
local tblTest1 = { 1, a = 2, 3 }
print("Test1 " .. table.getn(tblTest1))
此table的長度為2,可是明明是三個數值啊,這里要說明一下,getn 只能執行數值型的table,即數值索引的值.
忽略哈希值類型的鍵值對map,即不包含字符索引值
local tblTest1 = { b=1, a = 2, 3 }
print("Test1 " .. table.getn(tblTest1)) #輸出長度為1
local tblTest1 = { b=1, a = 2, c=3 }
print("Test1 " .. table.getn(tblTest1)) #輸出長度為0
local tblTest1 = { 1, 2, 3 }
print("Test1 " .. table.getn(tblTest1)) #輸出長度為3
在有個地方說明一下
--此table 雖然有[3]=6,也是數組型,但是不連續的,缺少了2的索引,所以輸出長度為1
local tblTest1 = { [3] = 6,c = 1, a = 2, [1]=3 }
print("Test1 " .. table.getn(tblTest1))
--此table 數值索引是連續 到2,所以輸出長度為2
local tblTest1 = { [2] = 6,c = 1, a = 2, 3 }
print("Test1 " .. table.getn(tblTest1))
=============================
獲取table長度,不區分數組和鍵值對
function table_length(t)
local leng=0
for k, v in pairs(t) do
leng=leng+1
end
return leng;
end
=================
特殊說明:
如果數組有一個"空洞"(就是說,nil 值被夾在非空值之間),那么 #t 可能是指向任何一個是 nil 值的前一個位置的下標(就是說,任何一個 nil 值都有可能被當成數組的結束)。
這也就說明對於有"空洞"的情況,table 的長度存在一定的 不可確定性。
關於nil的特別說明
local tblTest2 = { 1,nil,2}
對一個table中有nil值 取長度,會有很多不確定性,不同的luajit版本輸出的結果也不一樣
不要在 Lua 的 table 中使用 nil 值,如果一個元素要刪除,直接 remove,不要用 nil 去代替。
二)table.concat (table [, sep [, i [, j ] ] ])
對於元素是 string 或者 number 類型的表 table,
返回 table[i]..sep..table[i+1] ··· sep..table[j] 連接成的字符串。填充字符串 sep 默認為空白字符串。
起始索引位置 i 默認為 1,結束索引位置 j 默認是 table 的長度。如果 i 大於 j,返回一個空字符串。
local a = {1, 3, 5, "hello" }
print(table.concat(a)) -- output: 135hello
print(table.concat(a, "|")) -- output: 1|3|5|hello
print(table.concat(a, " ", 2, 4)) -- output: 3 5 hello
print(table.concat(a, " ", 4, 2)) -- output:
在介紹string字符串那邊有個字符串拼接,是用.. 這個符號進行的
local str = "a" .. "b" .. "c".......................... 推薦用concat
三)table.insert (table, [pos ,] value)
在(數組型)表 table 的 pos 索引位置插入 value,其它元素向后移動到空的地方。
pos 的默認值是表的長度加一,即默認是插在表的最后。
local a = {1, 8} --a[1] = 1,a[2] = 8
table.insert(a, 1, 3) --在表索引為1處插入3
print(a[1], a[2], a[3])
table.insert(a, 10) --在表的最后插入10
print(a[1], a[2], a[3], a[4])
-->output
3 1 8
3 1 8 10
四)table.remove (table [, pos])
在表 table 中刪除索引為 pos(pos 只能是 number型)的元素,並返回這個被刪除的元素,
它后面所有元素的索引值都會減一。pos 的默認值是表的長度,即默認是刪除表的最后一個元素。
local a = { 1, 2, 3, 4}
print(table.remove(a, 1)) --刪除速索引為1的元素
print(a[1], a[2], a[3], a[4])
print(table.remove(a)) --刪除最后一個元素
print(a[1], a[2], a[3], a[4])
-->output
1
2 3 4 nil
4
2 3 nil nil
五)table.sort (table [, comp])
local a = { 1, 7, 3, 4, 25}
table.sort(a) --默認從小到大排序
print(a[1], a[2], a[3], a[4], a[5])
-->output
1 3 4 7 25
按照給定的比較函數 comp 給表 table 排序,也就是從 table[1] 到 table[n],這里 n 表示 table 的長度。
比較函數有兩個參數,如果希望第一個參數排在第二個的前面,就應該返回 true,否則返回 false。
如果比較函數 comp 沒有給出,默認從小到大排序。
local function compare(x, y) --從大到小排序
return x > y --如果第一個參數大於第二個就返回true,否則返回false
end
table.sort(a, compare) --使用比較函數進行排序
print(a[1], a[2], a[3], a[4], a[5])
-->output
25 7 4 3 1
六)table.maxn (table)
返回(數組型)表 table 的最大索引編號;如果此表沒有正的索引編號,返回 0。
local a = {}
a[-1] = 10
print(table.maxn(a))
a[5] = 10
print(table.maxn(a))
-->output
0
5
七)table 判斷是否為空
大家在使用 Lua 的時候,一定會遇到不少和 nil 有關的坑吧。
有時候不小心引用了一個沒有賦值的變量,這時它的值默認為 nil。如果對一個 nil 進行索引的話,會導致異常。
如下:
local person = {name = "Bob", sex = "M"}
-- do something
person = nil
-- do something
print(person.name)
報錯person為nil了
然而,在實際的工程代碼中,我們很難這么輕易地發現我們引用了 nil 變量。
因此,在很多情況下我們在訪問一些 table 型變量時,需要先判斷該變量是否為 nil,例如將上面的代碼改成:
local person = {name = "Bob", sex = "M"}
-- do something
person = nil
-- do something
if person ~= nil then
print(person.name)
else
print("person 為空")
end
對於簡單類型的變量,我們可以用 if (var == nil) then 這樣的簡單句子來判斷。
我們如果要判斷table類型的對象是否為空,那如何判斷呢?
我們思考一下,判斷table是否為空有兩種情況:
第一種table對象為nil;
第二種table對象為{},代表沒有鍵值對,但不為nil。
那么我們一般的判斷邏輯就應該是 table == nil 或者 table的長度為0 就表示為空
下面我們看看以下例子:
local a = {}
local b = {name = "Bob", sex = "Male"}
local c = {"Male", "Female"}
local d = nil
if a == nil then
print("a == nil")
end
if b == nil then
print("b == nil")
end
if c == nil then
print("c == nil")
end
if d== nil then
print("d == nil")
end
if next(a) == nil then
print("next(a) == nil")
end
if next(b) == nil then
print("next(b) == nil")
end
if next(c) == nil then
print("next(c) == nil")
end
以上有幾個注意點,涉及到table類型的長度
(#a) --"#"表示為獲取table類型的長度,類似table.getn()
因為a為{},所以長度為0.
我們再看(#b) ,依然輸出的是0,但b是有值的啊。
我們再看(#c),輸出的是2,這個是怎么回事。這里就是之前在table類型的課程中已經介紹的獲取table的長度,
只是獲取的是 數組型的長度,不包含map型的。
我們再往下看 if a == nil then 在判斷 a是否為nil,明顯a不為nil
if next(a) == nil then中的next是什么意思呢?
next (table [, index])
功能:允許程序遍歷表中的每一個字段,返回下一索引和該索引的值。
參數:table:要遍歷的表
index:要返回的索引的前一索中的號,當index為nil[]時,將返回第一個索引的值,
當索引號為最后一個索引或表為空時將返回nil
next(a) 就是返回第一個索引的值,a的第一個索引是沒有值的,那么next(a) 就為nil
所以next方法經常用來判斷 table中是否有值。
下面的語句相信大家就能看懂了。
綜合以上代碼,我們判斷table是否為空,就不能簡單的判斷table長度是否為0,而是判斷索引值。
所以要判斷table是否為空應該按照以下進行判斷
function isTableEmpty(t)
return t == nil or next(t) == nil
end
八)ipairs和pairs的區別
為了看出兩者的區別,首先定義一個table:
a={"Hello","World";a=1,b=2,z=3,x=10,y=20;"Good","Bye"}
for i, v in ipairs(a) do
print(v)
end
輸出的結果是:
Hello
World
Good
Bye
可見ipairs並不會輸出table中存儲的鍵值對,會跳過鍵值對,然后按順序輸出table中的值。
再使用pairs對其進行遍歷:
for i, v in pairs(a) do
print(v)
end
輸出的結果是:
Hello
World
Good
Bye
1
10
2
20
3
可見pairs會輸出table中的值和鍵值對,並且在輸出的過程中先按順序輸出值,再亂序輸出鍵值對。
這是因為table在存儲值的時候是按照順序的,但是在存儲鍵值對的時候是按照鍵的哈希值存儲的,
並不會按照鍵的字母順序或是數字順序存儲。
對於a來說,如果執行print(a[3]),輸出的結果也會是Good。也就是說table並不會給鍵值對一個索引值。
也就是說ipairs只是按照索引值順序,打印出了table中有索引值的數據,沒有索引值的不管。
而pairs是先按照數組索引值打印,打印完成后再按照哈希鍵值對的鍵的哈希值打印它的值。
LuaJIT 2.1 新增加的 table.new 和 table.clear 函數是非常有用的。
前者主要用來預分配 Lua table 空間,后者主要用來高效的釋放 table 空間,並且它們都是可以被 JIT 編譯的。