一 表的特点
table是Lua'最复杂最强大'的数据结构,Lua本身并'不是面向对象'语言,面向对象中毒比较深的程序员'可以借助'table"完美地模拟"面向对象编程
+++++++++++++++'分割线'+++++++++++++++
1)表'table'是Lua语言中'最主要'-->'唯一'的强大'数据结构'
2)Lua语言以一种'简单、统一、高效'的方式表示'数组、集合、记录'和其它数据结构-->包括'function'和'table'
3)Lua语言中'表本质'是一种'辅助数组',类似awk中的"关联数组",数组的索引可以是'数字'、'字符串'、其它任意类型的值-->'nil除外'
4)table 的默认'初始索引'以' 1 '开始
5)table '不会固定长度'大小,有新数据插入时长度会'自动增长'
6)table 所有'元素之间',总是用逗号',' 隔开
7)表是一种'动态分配'的对象,程序只能'操作执行表'的引用('指针')
8)table 的变量只是一个'地址引用',对 table 的操作不会产生数据影响
二 表的操作
(1)构造器表达式创建表
a = {} -->'空构造器'创建'空表'
注意:'local a = {}' 与 'a = {}'的差异性
+++++++++++++"解读"+++++++++++++
local a = {} --> a是一个'局部变量'
type(a) --> a是另一个'新'的"全局"变量 --> 所以是'nil'
(2)初始化表
方式1:添加元素初始化
备注: a[k]形式'赋值' --> k是'变量' --> '实际等价' a["value"] = 10
补充: 常见的'三种'形式赋值: a.k = 10 <='等价'=> a["k"] = 10 、a[var] = 100
小结:表永远是'匿名的','表本身'和'保存表的变量'之间'没有固定'的关系
说明:对于一个表而言,当程序中'不再有指向它的引用-->引用计数'时,垃圾收集器会'最终删除'这个表并重用其占用的内存
方式2:表构造器初始化表
++++++++++++++++++'可以理解为数组初始化'++++++++++++++++++
days = {"Monday","Tueday","Wednesday","Thursday","Friday","Saturday","Sunday"} -->'简写'
等价: days = { [1]="Monday",[2]="Tueday","Wednesday" ...} --> "复杂形式"
特点:提前判断'表的大小',所以'运行速度更快'
备注: 索引是'数字',默认["数组"]第一个index是'1'
方式3:初始化记录式表
a = { x = 1, y = 9} ["简写"] <--> {["x"] = 1, ["y"] = 9} ["复杂"]
+++++++'等价方式'+++++++
a = {};a.x = 1 --> a["x"] = 1;a.y = 9
约定: key如果是'字符串string','声明'和'调用'最好不要加"双引号"
方式4:混用记录式和列表式
说明: 通过创建'嵌套表'和'构造器'构建更加复杂的'数据结构'
备注: 这种方式不能使用'负数'索引初始化元素
补充: table的'元素'可以是'任意'的数据结构,这里'只涉猎'table、键值对
方式5:通用方式
说明:'任何元素'来作为'索引'
operations = { ["+"] = "add", ["-"] = "sub", ["*"] = "mul", ["/"] = "div"}
说明:上述的四种方式是该种'通用方式'的特殊形式
(3)表索引
重点: 同一个'表中存储的值'可以具有'不同的类型索引',并可以'按需增长'以容纳'新'的元素
1)'未经初始化'的表元素为'nil'
2)将nil'赋值给表元素'可以将其'删除'
(4)两种方式引用(重点)
1)点分形式-->'a.name'-->清晰说明了'表是被当作结构体'使用的
备注:此时表是由'固定的、预先定义'的键组成的'集合'
2)形如a["name"]的字符串索引形式则说明了表可以使用'任意字符'作为键
+++++++++++'二者辨析'+++++++++++
1)a.x代表的含义是a["x"],即由字符串"x"索引的表
2)a[x]则是置由'变量x'对应的值索引的表
需求:当不能确定表'索引的真实数据类型'时,可以用'显式的'类型转换,避免在程序中引入'BUG'
a[tonumber(i)] --> 'number-->数值类型的键'
++++++++++++++++'分割线'++++++++++++++++
'整数和负类型'的'表索引'不存在上述问题,不需要'显式'转化
细节: 任何能够'被转化为整数的浮点数'都会被'转换成整数型'
(5)数组、列表、序列
+++++++++'可以理解为二者等价'+++++++++
数组 --> 'java' --> 整数为索引,区别于其它语言,从'1'开始 -->实际只需要'索引是正整数'即可
列表 --> 'python'
+++++++++++++++'序列概念'+++++++++++++++
序列 --> 列表中'所有的元素'都不为'nil',也即列表中'不存在'空洞'hole' -->在列表的基础上继续'升华'
说明: 如果明确是'序列',则可以通过'#'来统计'列表的长度'
① 禁止对不连续的数组表求长度
hole --> 出现'空洞' --> #arrary '遇nil'即'停止'统计
② 打印序列的内容
备注: 数组'遍历'的时候'有用' ->'#table' -->统计个数
③ 序列的几个常用的操作
④ 补充
Lua语言中: 一个'为nil的字段'和一个'不存在的元素'没有区别
+++++++++++'解释'+++++++++++
a = {1, 2, nil, nil} '等价' a + {1, 2} --> '长度为3'
(6)遍历表
① pairs迭代器遍历表中的键值对
++++++++++'特点'++++++++++
1)受限于表在Lua语言中的'底层实现机制',遍历过程中元素出现的'顺序可能是随机(random)的'
备注:所谓的随机'具体表现'为每次打印都可能'不同';'但是'遍历的过程中每个元素'会且只会出现一次'
遍历结果'result'是'随机的' --> 多次'遍历的结果'不一样
② ipairs迭代器遍历列表
++++++++++++++多次执行的'结果'++++++++++++++
1)对于'列表',执行该脚本的结果是'幂等的',始终保持着'顺序'
2)'ipairs方式'在迭代的过程中,遇到'hole-->即:nil值'则退出
+++++++++++++'区别在于'+++++++++++++
核心:遍历'列表'的时候二种方式都可以'按照顺序遍历' -->多次执行顺序结果是一致的-->'幂等性'
1)pairs可以遍历到表中所有的key,对于'key的类型没有要求',遇到'nil时可以跳过-->jump',不会影响后面的遍历
2)ipairs遍历时'只能取key为整数值',遇到nil时'终止-->end'遍历 -->所以只适合'列表[数组]'-->'index为正整数'
③ 数值型for循环
说明: 这种方式只针对'序列'
④ 最佳实践
1)如果'是序列'且需要'顺序输出-->index的顺序',优先采用'ipairs'和'for数值循环'
2)如果是'一般table-->非列表',并且'没有顺序'的需求,采用'pairs'进行遍历
三 表标准库
说明: 表标准库提供了'操作列表'和'序列'的一些常用函数
① insert
特点:向'序列'的'指定位置(pos)插入值为value'的一个元素,'pos参数可选','不指定'位置,则默认在序列的'末尾'
② remove
+++++++++++++++'remove特点'+++++++++++++++
1)删除指定位置'pos'的元素
2)返回'return'-->序列'指定位置'的元素
备注1: pos位置'后'的元素会'被前移',pos参数可选('options')
备注2: 默认为table长度,即从'最后一个元素删起'
++++++++++++'区别'++++++++++++
'array[index] =nil' 和 'table.remove(array,index)'的区别
③ move
Lua(>='5.3引入')中的table.move
+++++++++++++'分割线'+++++++++++++
原型:table.move(a1,f,e,t[,a2])
函数作用: 把表a1中从下标f到e的value移动到表a2中,位置为a2下标从t开始
函数参数: 表a1,a1下标开始位置f,a1下标结束位置e,t选择移动到的开始位置(如果没有a2,默认a1的下标)
④ concat
concat: 输出一个列表中'元素连接成的字符串'
⑤ sort
table.sort()函数对给定的table进行'升序排序'
⑥ getn
当我们获取 table 的长度的时候无论是'使用 #' 还是 'table.getn' 其都会在'索引中断-->不连续'的地方停止计数,而导致'无法正确取得 table 的长度'
⑦ maxn
table.maxn 在 'Lua5.2 之后'该方法已经'不存在了'-->'lua5.3'中我们定义了 'table_maxn' 方法来实现,获取 table 中的'最大值'
++++++++++'lua5.3'的自定义实现++++++++++
function table_maxn(t)
local mn = nil
for k, v in pairs(t) do
是否'中断'或者'最后'一个元素
if(mn==nil) then
mn=v
end
if mn < v then
mn = v
end
end
return mn
end
四 lua操作table注意事项
1)最好不要'数组'和hash'混用',否则'#取长度'会乱七八糟
2)数组中'不要存储nil值'
3)使用table.remove,'不要使用置nil'
五 常见的需求
① 判断table中是否包含某个value
-- 判断table中是'否包含某个value'
function is_include(value, tab)
for k,v in pairs(tab) do
if v == value then
return true
end
end
return false
end
② 判断lua table是否为空表
需求:lua table中如何'判断为空表' --> '靠lua内置的next函数'
if next(a) == nil then
特点:next其实就是'pairs遍历table时'用来取下一个内容的函数
+++++++++++'最佳实践-->封装成函数'+++++++++++
1)在项目的'module中最好封装'一下,免得module'本地也有next函数',于是封装后'判断'的lua table是否为空的函数如下
function table_is_empty(t)
return _G.next( t ) == nil
end