Lua 数据类型 —— 表

Lua 数据类型 —— 表

一、简介

表永远是匿名的,表本身和保存表的变量之间没有固定联系。

对于一个表而言,当程序不再有变量指向他时,垃圾收集器会终究删去这个表并重用其占用的内存。

Lua 不会进行隐藏复制或创立新表,操作的都是指向表的指针。

二、元素

1、键

表的键能够具有不同的类型,并且能够按需增加容纳新的元素

假如 table 是一个序列,则下标是从 1 开端,切记不是从 0 开端。

不能用 nil 作为键,否则会抛 table index is nil 过错

2、值

表的值能够经过赋值 nil 进行删去原有的项

3、获取元素

第一种: table[key]

类似 java 、 kotlin 的 map 取值 ,假如 key 对应的值并不存在,则返回 nil

第二种: table.key

这种取值等价于 table[“key”] ,记得这儿是字符串,而不是 table[key]

table[key] 和 table.key 区别

两种方式从运用的视点来说是没有任何的区别

从语义上说,table[key] 表示表能够运用恣意字符串作为键,table.key 表示表是被当作结构体运用,表实际上由固定的,预先界说的键组成集合

k = "jiang"
a = {}
a[k] = 28
a[29] = "xiao"
-------------
-- 这儿的 a 表有以下内容
-- "jiang" --> 28
-- 29      --> "xiao"
-------------
print(a[k])         --> 28
print(a.k)          --> nil         a.k 相当于 a["k"],所以就没有值
print(a.jiang)      --> 28          a.jiang 相当于 a["jiang"]

4、数值作为键

任何能够被转换为整型的浮点数都会被转换成整型数(和之前数值一章的共享不谋而合)

c = {}
c[2.0] = 10
print(c[2])     --> 10 

三、表结构器

无论用哪种结构的表,都能够用 nil 赋予到对应的 key 删去对应的值。

空结构器

table1 = {}

列表式

这种方式运行速度更快一点(相比于记载式),由于能够提前判别表大小。键会是从 1 开端往上递增的值。

-- 列表式,键会是从 1 开端往上递增的值
table2 = { "Jiang", "peng", "yong", }   -- 最有的逗号是合法的,但不会被当作元素
--> 表结构
--> 1 -- "Jiang"
--> 2 -- "peng"
--> 3 -- "yong"

记载式

key 值会被当作字符串运用,例如下面的 a key,终究形态是 “a” ,所以能够运用 table3.a 。

table3 = { a = "1", b = 20, c = "3" }
print("table3 length: " .. #table3)         --> table3 length: 0        -- 由于 lua 表并不会进行保护长度,只会记载序列的下标作为表的长度
print(table3.a, table3["b"], table3.c)      --> 1	20	3
--> 表结构
--> "a" -- "1"
--> "b" -- 20
--> "c" -- "3"

混合两种

混合运用并不会有抵触,列表式的记载会从下标为 1 开端累加,记载式则仍是继续运用自己的 key 。

table4 = { name = "江澎涌", { 170, "cm" }, age = 28, { "专业", "软件工程" } }
print("table4 length: " .. #table4)                             --> table4 length: 2
print(table4.name, table4.age, table4[1][1], table4[2][2])      --> 江澎涌	28	170	软件工程

通用式

这种方式最为灵活,尽管运用起来费事些。

即经过方括号括起来的表达式显现地指定每一个索引

能够运用一些 “特别字符” 或 “负数” 一类进行作为 key ,也能够在方括号中运用核算表达式,运用其结果作为 key 值。

table5 = { [-1] = "j", "i", ["a"] = "a", "n", c = "g" }
print("table5 length: " .. #table5)         --> table5 length: 2
print(table5[1], table5[-1])                --> i	j

四、表长度

table 没有保护一个类似 java 、 kotlin 中 map 的 size 的尺度。所以我们需求自己进行保护变量进行存储,能够存放鄙人面几个当地:

  1. 存储在一个常量中
  2. 存放在其他变量或数据结构中
  3. 存放在 table 中某个非数值类型的字段中,一般运用 “n” 作为 key

1、序列长度

序列是指不存在空泛(nil,一切的元素都不为nil)的有序列表。

序列能够运用 #table 进行获取长度。不包括数值类型 key 的 table 便是长度为零的序列。

处理有空泛的列表则需求自行保护长度,否则 #table 不行靠

从下面代码段的最终能够看出,存在空泛的 table 长度现已不行靠了。由于存在 nil 的行为程序欠好界说程序猿想表达的目的是删去(则长度应该不算该值),仍是有意让该 key 值为一个 nil 值(则长度应该算该值)。总而言之,假如能保证是无空泛序列的话,则能够运用 “#” 核算长度,否则自行保护。

sequence = {}
for i = 1, 10 do
    sequence[i] = i * i;
end
print("sequence size(没有空泛): " .. #sequence)           --> sequence size(没有空泛): 10
sequence[5] = nil   
print("sequence size(存在空泛,[5] = nil ): " .. #sequence)   --> sequence size(存在空泛,[5] = nil ): 10 (其实没有 10 个值)
sequence[10] = nil
print("sequence size(存在空泛,[10] = nil ): " .. #sequence)  --> sequence size(存在空泛,[10] = nil ): 9 (删去了最终一个,其实这儿第 5 个和第 10 个都被删去了)
sequence[11] = 11 * 11
print("sequence size(存在空泛,[11] = 11*11 ): " .. #sequence)    --> sequence size(存在空泛,[11] = 11*11 ): 11
sequence[10] = 100
print("sequence size(存在空泛,[100] = 100 ): " .. #sequence)     --> sequence size(存在空泛,[100] = 100 ): 11

2、遍历核算长度

利用 pairs 会将一切的元素都遍历一次的特性,进行核算 table 中有多少元素。pairs 鄙人一末节进行共享

function table_leng(t)
  local leng = 0
  for k, v in pairs(t) do
    leng = leng+1
  end
  return leng;
end

五、表遍历方式

三种遍历方式

1、paris

因底层完成问题,pairs 不会保证顺序,可能每次遍历结果都不同,但每个元素一定会出现一次

table5 = { 10, print, x = 12, k = "hi" }
for k, v in pairs(table5) do
    print(k, "-->", v)
end
--> 1	-->	10
--> 2	-->	function: 0x109cdeac0
--> x	-->	12
--> k	-->	hi 

2、ipairs

列表能够用 ipairsipairs 会保证顺序进行,不是数字或是不接连(有空泛)则会被越过

print("ipairs 遍历")
table6 = { 10, print, 12, "hi"}
table6[10] = 111
table6["a"] = 100
for i, v in ipairs(table6) do
    print(i, "-->", v)
end
--> ipairs 遍历
--> 1	-->	10
--> 2	-->	function: 0x106f13ac0
--> 3	-->	12
--> 4	-->	hi

3、序列下标遍历

table6 = { 10, print, 12, "hi" }
for i = 1, #table6 do
    print(i, "-->", table6[i])
end
--> 序列数值遍历
--> 1	-->	10
--> 2	-->	function: 0x106f13ac0
--> 3	-->	12
--> 4	-->	hi

六、安全操作符

Lua 中没有安全操作符,不像 kotlin 中有 ?. 这样的操作,但能够经过 nullable or default 的格式进行操作,例如下面操作

E = {}
company = { }
zip = (((company or E).director or E).address or E).zipcode

类似 kotlin 中

var zip = company?.director?.address?.zipcode

举个比如

这儿便是利用了 or 的特性

print("模仿安全操作: ")
----- 安全操作 -------
E = {}
company = nil
zipcode = (((company or E).director or E).address or E).zipcode or "default"
print(zipcode)          --> default
company = { director = { address = { zipcode = "10080" } } }
zipcode = (((company or E).director or E).address or E).zipcode or "default"
print(zipcode)          --> 10080

七、表规范库操作

只针对列表、序列操作,一切的操作下标开端是 1

1、insert(table, index, item)

在 table 的 index 下标刺进 item ,假如 index 越界了,则会抛出 bad argument #2 to 'insert' (position out of bounds)

table7 = { 10, 20, 30 }
showTable(table7)       --> [1]=10, [2]=20, [3]=30, 
table.insert(table7, 2, 909)
showTable(table7)       --> [1]=10, [2]=909, [3]=20, [4]=30,

假如不设置刺进下标,则默许插在结尾

table.insert(table7, 50)
showTable(table7)       --> [1]=10, [2]=909, [3]=20, [4]=30, [5]=50, 

2、remove(table, index)

移除 table 的 index 下标的元素,会有一个返回值,即被移除的元素,则会抛出 bad argument #1 to 'remove' (position out of bounds)

table.remove(table7, 3)
showTable(table7)       --> [1]=10, [2]=909, [3]=30, [4]=50, 

假如不设置下标参数,则移除结尾

table.remove(table7)
showTable(table7)       --> [1]=10, [2]=909, [3]=30, 

3、move(table, startIndex, endIndex, newIndex)

move 仅仅担任移动元素,并不会顺便删去,假如需求删去,则需求自行对元素进行设置为 nil

将 table 中从 startIndex 到 endIndex 的元素( [ startIndex, endIndex ] ),复制到 newIndex 下标开端。

move 模仿在开头刺进元素

table.move(table7, 1, #table7, 2)
table7[1] = 0
showTable(table7)        --> [1]=0, [2]=10, [3]=909, [4]=30, 

move 模仿删去第一个元素,记得要将结尾进行 nil 删去,否则最终元素还存在

table.move(table7, 2, #table7, 1)
table7[#table7] = nil
showTable(table7)          --> [1]=10, [2]=909, [3]=30, 

运用 move 能够进行 table 间的复制,即运用 table(table, startIndex, endIndex, newIndex, newtable)

需求多传入一个 table , 第四个参数指定的是 table 开端赋值的下标

-- move 将表 a 复制到表 b , 能够经过 (#b + 1) 将元素接到表 b 结尾
table8 = { 20, 20, 30, 40, 50 }
table.move(table7, 1, #table7, #table8 + 1, table8)
showTable(table7)   --> [1]=10, [2]=909, [3]=30, 
showTable(table8)   --> [1]=20, [2]=20, [3]=30, [4]=40, [5]=50, [6]=10, [7]=909, [8]=30, 

4、sort(table, sortfunction)

对 table 排序

table9 = {
    { name = "xiao peng you", age = 20 },
    { name = "zinc", age = 27 },
    { name = "jiang peng yong", age = 28 },
}
for i, v in ipairs(table9) do
    print(i, "-->", v.name, "--", v.age)
end
--> 1	-->	xiao peng you	--	20
--> 2	-->	zinc	--	27
--> 3	-->	jiang peng yong	--	28
table.sort(table9, function(a, b)
    return #a.name > #b.name
end)
for i, v in ipairs(table9) do
    print(i, "-->", v.name, "--", v.age)
end
--> 1	-->	jiang peng yong	--	28
--> 2	-->	xiao peng you	--	20
--> 3	-->	zinc	--	27

值得注意

运用 insertremove 函数时,假如针对中间元素进行操作,则会导致后面的元素移动,会有额定的开销。

八、写在最终

Lua 项目地址:Github传送门 (假如对你有所协助或喜爱的话,赏个star吧,码字不易,请多多支撑)

公众号查找 “江澎涌” 能够更快的获取到后续的更新文章

Lua 数据类型 —— 表