L3-07. table类型
一. 基本知识
1. table是Lua的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
2. Lua的table数据类型,可以用任意类型来作数组的索引,但这个值不能是 nil。
3. Lua table 是不固定大小的,是一个动态表, 自动随心扩容
4. Lua也是通过table来解决模块、包和对象的。 例如string.format表示使用"format"来索引table string。
二. table的构造
1. table使用"{}"大括号进行创建表或初始化表
--定义一个空表 local mytable = {} -- 指定值 mytable[1]= "Lua" -- 移除引用 mytable = nil -- lua 垃圾回收会释放内存
--定义并初始化一个表, 这种写法可以避免添加值时, 进行重新哈希, 可以有效提高执行效率 local t2 = {"nil","nil","nil","nil"}
2. 避免重新哈希, 提高执行效率
--Lua也会为哈希部分创建一个大小为4的数组。例如,执行下面的代码需要2.0秒: for i = 1, 1000000 do local a = {} a[1] = 1; a[2] = 2; a[3] = 3 end --如果在创建表时给定正确的大小,执行时间可以缩减到0.7秒: for i = 1, 1000000 do local a = {true, true, true} a[1] = 1; a[2] = 2; a[3] = 3 end
3. 如果以正整数作为key存储的key,与索引重复,则以索引的值为准
4. 可以用任意类型当表的key, 除了nil, 和布尔类型
--表的赋值 local a = "b" local table1 = {["false"] = 123,["nil"] = 456,a = 10,[a] = 20} print(table1["false"]) --输出结果: 123 print(table1["nil"]) --输出结果: 456 print(table1.a) --输出结果: 10 print(table1[a]) --输出结果: 20 print(table1["b"]) --输出结果: 20 print(table1[b]) --输出结果: nil print("任意类型做键值, 除nil和布尔类型") --任意类型做键值, 除nil和布尔类型 local t = {} local f = function() end local tbl = {[t] = 123, [f] = 456} print(tbl[t]) --输出结果: 123 print(tbl[f]) --输出结果: 456
三. table指针
1. 当我们为 table a 设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存
local tbl = {} --定义并初始化一个空表 local tab = tbl --将tbl保存的地址赋值tab tab["name"] = "名字" --设置tab表中字段的值 --因为两个变量都指向同一个地址(表), 改变tab表中字段的值, tbl也会跟着改变 print(tbl.name) --输出tbl表中字段的值, 输出结果: 名字 tbl = nil --将tbl的值设置为nil --因为上面设置的是tbl的值, 而tab还是指向了表的地址 print(tab.name) --输出结果: 名字
四. table中存在nil的注意事项
1. #获取数组的长度, 遇到nil会立即停止计数, 而导致无法正确取得 table 的长度
local t = {1,nil,2,nil,nil} print(#t) --输出结果为: 1 --解决办法:将nil赋值一个字符串nil或者用""当空 local t = {1,"nil",2,"nil","nil"} print(#t) --输出结果为: 5
五. lua表的实现方式
1. 一般情况下不需要了解表的实现方式就能够使用它, 但是了解一些表的实现细节可以更好的掌握性能的开销情况
2. Lua表的内部包含两个部分:数组部分和哈希部分
local t = {1,2,3}--保存在数组部分 local t = {[1] = 1, [2] = 2, [3] = 3} --保存在hash部分 --"#"可以获取数组的长度, 也可以获取hash部分数字字段的长度
3. 重新哈希的原理
例子: --这种写法要比下面的写法在性能上要快, 因为在开始时, 就确定了数组的大小, 不超过这个大小不会重新哈希 local t = {true, true, true, true, true} -------------------------------------------------- local t = {} --当Lua创建空表时,两个部分的大小都是0 t[1] = true --触发一次重新哈希, 将数组部分设置为2^0, 哈希部分是0 t[2] = true --触发一次重新哈希, 将数组部分设置为2^1, ... t[3] = true --触发一次重新哈希, 将数字部分设置为2^2, ... t[4] = true --不会触发重新哈希, 重新哈希则只会发生在表完全填满后 t[5] = true --触发一次重新哈希, 将数组部分设置为2^3, ... 重新哈希时, 都会创建原本大小的2倍 结论: 对于大的表来说,初期的几次重新哈希的开销被分摊到整个表的创建过程中 一个包含三个元素的表需要三次重新哈希,而一个有一百万个元素的表也只需要二十次。 但是当创建几千个小表的时候,重新哈希带来的性能影响就会非常显著。 Lua也会为哈希部分创建一个大小为4的数组。例如,执行下面的代码需要2.0秒 for i = 1, 1000000 do local a = {} a[1] = 1; a[2] = 2; a[3] = 3 end 如果在创建表时给定正确的大小,执行时间可以缩减到0.7秒 for i = 1, 1000000 do local a = {true, true, true} a[1] = 1; a[2] = 2; a[3] = 3 end
4. 如果你遍历一个表并清除其所有项(也就是全部设为nil),表的大小不会缩小。但是此时,如果你需要插入新的元素,表的大小将会被调整。多数情况下这都不会成为问题,但是,不要指望能通过清除表项来回收内存:最好是直接把表自身清除掉。
六. table函数库
1. table.concat()
--[=[ local str = table.concat (list [, sep [, i [, j]]]) 语法: local str = table.concat (list [, sep [, i [, j]]]) 译文: local 合成字符串 = table.concat (数组 [, 合成字符串后每个元素的分隔符 [, 开始位置 [, 结束位置]]]) 功能: 将数组中的第一维合并成字符串, 其所有元素都是字符串或数字, 否则合并会报错 参数: 参数list(必选): 数组 参数sep(可选): 合成字符串后每个元素的分隔符, 默认为"" 参数i(可选): 开始位置, 如果i>#list则返回空串, i > j 也返回空串, 默认值为:1 参数j(可选): 结束位置, j的默认值是 #list 返回值: 返回合成后的字符串内容 --]=] --[[ lua中的每个字符串都是单独的一个拷贝,拼接两个字符串会产生一个新的拷贝 如果拼接操作特别多,就会影响性能,所以对于密集型的字符并接,table.concat 比用 ".." 连接更高效。 --]] --例子1: local t = {123,456,567,678} print(table.concat(t,",",1,#t)) --输出结果: "123,456,567,678" --例子2: t = {1,2,3,nil,4} --print(table.concat(t,",",1,#t)) --报错, 合并的数组元素必须是字符串或数字, 其它类型都会报错
2. table.move()
--[=[ table.move (a1, f, e, t [,a2]) 语法: table.move (a1, f, e, t [,a2]) 译文: table.move (a1, f, e, t [,a2]) 功能: 把表a1中从下标f到e的value移动到表a2中,位置为a2下标从t开始 参数: 参数a1(必选): 被复制的数组或复制到的数组(取决于是否有a2参数) 参数f(必选) : 复制开始位置 参数e(必选) : 复制结束位置 参数t(必选) : 接收的位置(从a2或a1中的第几个位置开始覆盖, 取决于是否有a2参数) 参数a2(必选): 接收的数组, 如果有此参数, 则将复制的内容按照接收位置依次复制到此数组(a2) 如果没有此参数, 则将复制的内容按照接收位置依次复制到a1数组 返回值: 无 --]=] tbl = {"a","b","c"} newtbl = {1,2,3,5} --将tbl表中的元素b,c复制到newtbl表中,从newtbl表中的第二个元素开始覆盖 table.move(tbl, 2, 3, 2, newtbl) print(table.concat(tbl,",")) --输出结果: a,b,c print(table.concat(newtbl,",")) --输出结果: 1 b c 5 --将tbl表中的元素a,b,c复制到tbl表中(如果没有第二个table则会复制带自身),从第三个元素开始覆盖 table.move(tbl, 1, 3, 3) print(table.concat(tbl,",")) --输出结果: a b a b c
3. table.soft()
--[=[ table.soft (arr, function) 语法: table.soft (arr, function) 译文: table.soft (arr, function) 功能: 根据函数规则, 对arr数组进行排序 参数: 参数arr(必选): 要排序的数组 参数function(可选): 排序的规则, 为比较false时才会交换位置, 如果不填写默认为"<", 进行升序排序 当比较的两个元素相等的时候,比较函数一定要返回false,返回true会报错 table.sort会根据你返回的bool来判断两个value是否保持原来的顺序 返回值: 无 --]=] --对数组进行排序 --例子1: local t1 = {8,10,5,2,6} --默认为"<", 进行升序排序 table.sort(t1) print(table.concat(t1, "|")) --例子2: local t1 = {8,10,5,2,6} --进行降序 table.sort(t1, function(a,b) return a > b end) print(table.concat(t1, "|")) --例子3: local t1 = { {id = 1, num = 88, level = 30}, {id = 2, num = 66, level = 10}, {id = 3, num = 66, level = 90}, {id = 4, num = 99, level = 20}, {id = 5, num = 66, level = 90} } table.sort(t1, function(a, b) return a.num < b.num end) for _, v in pairs(t1) do print(string.format("id = %d, num = %d, level = %d", v.id, v.num, v.level)) end --例子4: 多个条件进行排序, 在排序中两个值相等必须返回的是false才不会报错 print("多个条件进行排序") local t1 = { {id = 1, num = 88, level = 30}, {id = 2, num = 66, level = 10}, {id = 3, num = 66, level = 5}, {id = 4, num = 99, level = 20}, {id = 5, num = 66, level = 300} } table.sort(t1, function(a, b) --如果仅相等让他返回false, 这种写法也可以not (a.num >= b.num) if a.num < b.num then return true elseif a.num == b.num then return a.level < b.level elseif a.num > b.num then return false end end) for _, v in pairs(t1) do print(string.format("id = %d, num = %d, level = %d", v.id, v.num, v.level)) end --例子5: 根据value排序key print("value的大小排序key") local tbl = {[1] = 40303, [3] = 40304, [5] = 40305, [7] = 40301, [9] = 40302} local t = {1,3,5,7,9} table.sort(t, function(a,b) return tbl[a] < tbl[b] end) print(table.concat(t,"|"))
4. table.insert()
--[=[ table.insert (list, [pos,] value) 语法: table.insert (list, [pos,] value) 译文: table.insert (数组, [插入位置,] 插入值) 功能: 在数组指定位置插入元素, 会将插入位置后的数组元素依次往后移 参数: 参数list(必选): 插入元素的数组 参数pos(可选): 插入数组的位置, 默认为: 末尾插入 参数value(可选): 插入的数组元素(值) 返回值: 无 --]=] -- --例子1: local table1 = {"学","Lua","中"} table.insert(table1,2,"习") print(table.concat(table1)) --输出结果: 学习Lua中
5. table.remove()
--[=[ table.remove (list [, pos]) 语法: table.remove (list [, pos]) 译文: table.remove (数组 [, 删除位置]) 功能: 删除数组中指定位置的元素, 会将删除位置后的数组元素往前移 参数: 参数list(必选): 删除元素的数组 参数pos(可选): 删除数组中元素的位置, 默认为: #list, 删除数组中的最后一个元素 返回值: 被移除的值 --]=] --例子1: local tbl = {"a", "b", "c", "d", "e"} local value = table.remove(tbl, 4) print(value) --输出结果: d print(table.concat(tbl,", ")) --输出结果: a, b, c, e value = table.remove(tbl) print(value) --输出结果:e print(table.concat(tbl,", ")) --输出结果: a, b, c
6. table.pack()
--[=[ local arr = table.pack(...) 语法: local arr = table.pack(...) 译文: local 数组 = table.pack(列表) 功能: 将列表打包成一个table, 其中包含一个字段 n,表示该表的长度 参数: 可变长参数...(必选): 要打包的列表 返回值: 返回一个打包好的数组 --]=] --例子1: function table_pack(param, ...) local arg = table.pack(...) print(arg.n) for i = 1, arg.n do print(i, arg[i]) end end table_pack("test", "param1", nil, "param3")
7. table.unpack()
--[=[ local list = table.unpack(arr) 语法: local list = table.unpack(arr) 译文: local 列表 = table.pack(数组) 功能: 将数组解包成序列, 如果存在nil将中断解包, 然后返回 参数: 参数arr(必选): 待解包的数组 返回值: 返回一个列表, (每个元素是一个返回值) --]=] --例子1: print(table.unpack({1,2,3,nil,5,nil})) --输出结果: 1 2 3
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了