概述
- Lua 没有独立的数组或字典类型,而是通过其强大的 table 数据结构来统一实现
- table 是 Lua 的核心,可以看作是关联数组(associative array),键(key)可以是任意类型(除了 nil),值(value)也可以是任意类型
- 当键为数字时,table 表现为数组;当键为字符串或其他类型时,table 表现为字典
背景
- Lua 是一种轻量级脚本语言,最初由巴西里约热内卢天主教大学的研究小组于 1993 年开发
- 其设计目标是嵌入应用程序,提供灵活的扩展和定制功能
- table 作为 Lua 中唯一的数据结构,集数组、字典、集合、对象等多种功能于一身,是其灵活性的关键
- Lua 数组实现
- Lua 中的数组本质上是使用数字作为键的 table
- 数组索引通常从 1 开始(虽然也可以使用 0 或负数)
数组与字典的详细实现与实践案例
1 ) 实践案例 1:一维数组
--- 定义一个数组(使用数字键)
local fruits = {"apple", "banana", "orange"}
--- 访问元素(索引从1开始)
print(fruits[1]) -- 输出: apple
print(fruits[2]) -- 输出: banana
--- 遍历数组(使用 ipairs,保证顺序)
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
--- 输出:
--- 1 apple
--- 2 banana
--- 3 orange
--- 添加元素
table.insert(fruits, "grape")
print(fruits[4]) -- 输出: grape
--- 删除元素
table.remove(fruits, 2) -- 删除索引为2的元素
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
--- 输出:
--- 1 apple
--- 2 orange
--- 3 grape
特点:索引为数字,通常从1开始;#fruits 或 table.getn(fruits) 可获取数组长度(但对稀疏数组可能不准确);ipairs 遍历时会按数字索引顺序迭代,遇到 nil 值会停止
2 ) 实践案例 2:多维数组
--- 定义一个 3x3 的二维数组(即 table 的 table)
local matrix = {}
for i = 1, 3 do
matrix[i] = {} -- 每一行是一个新的 table
for j = 1, 3 do
matrix[i][j] = i * j -- 填充元素
end
end
--- 访问二维数组元素
print(matrix[2][3]) -- 输出: 6
--- 遍历二维数组
for i = 1, 3 do
for j = 1, 3 do
print("matrix[" .. i .. "][" .. j .. "] = " .. matrix[i][j])
end
end
特点:通过嵌套 table 实现;matrix[i][j] 等价于 ((matrix[i])[j])。
Lua 字典实现
Lua 中的字典同样使用 table,但键通常是字符串或其他非数字类型
1 ) 实践案例 1:基础字典操作
--- 定义一个字典(使用字符串键)
local person = {
name = "Alice", -- 等价于 ["name"] = "Alice"
age = 30,
city = "New York"
}
--- 访问元素
print(person.name) -- 输出: Alice
print(person["age"]) -- 输出: 30
--- 修改值
person.age = 31
print(person.age) -- 输出: 31
--- 添加键值对
person.job = "Engineer"
person["salary"] = 50000
--- 删除键值对(通过赋值为 nil)
person.city = nil
--- 遍历字典(使用 pairs,顺序不定)
for key, value in pairs(person) do
print(key, value)
end
--- 可能的输出:
--- name Alice
--- job Engineer
--- salary 50000
--- age 31
特点:键为字符串(或其它类型),通过 key 访问 value;pairs 遍历时顺序不确定。
2 ) 实践案例 2:混合使用(数组和字典部分)
local mixed_table = {
"first", -- 索引1
"second", -- 索引2
name = "Bob", -- 字典部分
age = 25 -- 字典部分
}
--- 访问数组部分
print(mixed_table[1]) -- 输出: first
print(mixed_table[2]) -- 输出: second
--- 访问字典部分
print(mixed_table.name) -- 输出: Bob
print(mixed_table["age"]) -- 输出: 25
--- 遍历混合 table(需要注意 pairs 和 ipairs 的行为)
print("Using pairs (all key-value pairs):")
for k, v in pairs(mixed_table) do
print(k, v)
end
print("Using ipairs (array part only, stops at first nil):")
for i, v in ipairs(mixed_table) do
print(i, v)
end
特点:一个 table 可以同时包含数字索引和字符串索引(或其它类型索引)的元素
数组与字典实现对比表
| 特性 | Lua 数组 (Table with Numeric Keys) | Lua 字典 (Table with String/Other Keys) |
|---|---|---|
| 键类型 | 通常是数字 (通常从1开始) | 通常是字符串或其他类型 |
| 访问方式 | arr[index] 或通过 ipairs 遍历 | dict.key 或 dict[“key”] 或 pairs 遍历 |
| 遍历顺序 | ipairs 保证顺序 (从1开始连续) | pairs 不保证顺序 |
| 长度获取 | #arr (对连续数组有效) | 无内置函数,需手动计算或使用 pairs 计数 |
| 主要用途 | 存储有序、同类型元素 | 存储无序、键值对映射关系 |
| 底层实现 | 与字典相同,都是 table | 与数组相同,都是 table |
下面是数组与字典实现逻辑的核心对比:
| 维度 | 数组实现 | 字典实现 |
|---|---|---|
| 索引规则 | 仅使用连续整数(默认从1开始) | 支持任意非nil类型(字符串/数字等) |
| 遍历方式 | 优先ipairs、for i=1,#t遍历 | 必须使用pairs遍历 |
| 内置方法适配 | 兼容table.insert/sort等数组方法 | 不支持table标准数组方法 |
| 典型使用场景 | 有序存储同类型数据 | 键值对映射存储异构关联数据 |
数组实现扩展案例
1 ) 基础声明与访问:使用大括号直接声明,默认索引从1开始,可手动指定非连续负索引
--- 声明一维数组
local arr = {"Lua", "Table", "Array"}
print(arr[1]) --> Lua
--- 声明负索引数组
local neg_arr = {}
neg_arr[-1] = "negative index"
2 )动态扩容与修改:直接新增索引指向值即可扩容,使用table.insert/table.remove完成指定位置操作[25]
table.insert(arr, 2, "Demo")
--> arr变为 {"Lua","Demo","Table","Array"}
3 )多维数组实现:通过表嵌套表来构建,结合嵌套循环初始化与访问
local matrix = {}
for i=1,3 do
matrix[i] = {}
for j=1,3 do matrix[i][j] = i*j end
字典实现实践案例
1 ) 基础声明与访问:支持括号索引与点语法两种写法,点语法仅适用于字符串键且键名不含特殊字符
--- 声明键值对字典
local user = {name="Alice", age=24, [true]="valid"}
print(user.name) --> Alice (点语法)
print(user[true]) --> valid (括号索引)
2 ) 增删改操作:新增指定索引值完成添加操作,赋值nil即可移除字典元素[10]
user.gender = "female" --新增键值对
user.age = 25 --修改值
user.age = nil --删除age键值对
3 ) 有序遍历字典:将字典键转为数组后排序,基于有序数组遍历获取对应值
local keys = {}
for k in pairs(user) do table.insert(keys,k) end
table.sort(keys) --按键排序
for _,k in ipairs(keys) do print(k, user[k]) end
混合存储实践
Lua允许在同一个表内同时存储数组与字典逻辑,数组索引与字典键互不干扰[3]
local mix_data = {
"first element", "second element",
title = "Mixed Table Demo",
version = "5.4"
}
Lua 数组与字典的核心机制
Lua 仅用 table 一种数据结构实现了数组(顺序表)和字典(关联数组),其设计遵循灵活性优先原则:
- 数组本质:以连续整数为键的 table。索引默认从 1 开始(符合数学传统),但支持任意起点(如 0 或负数)
- 字典本质:键值对集合,键可为 string、number 甚至 table,值无类型限制[9]。
- 混合存储:同一 table 可同时包含数组部分和哈希部分,例如:
mixed = { "apple", "banana", color = "yellow", price = 2.5 } -- 前两项为数组,后两项为字典
高级案例实践
1 ) 案例 1:游戏地图网格系统(二维数组)
需求:存储 3x3 地图格子类型(0=空地,1=障碍)并快速访问
local map = {}
for i = 1, 3 do
map[i] = {} -- 每行初始化新数组
for j = 1, 3 do
map[i][j] = (i == j) and 1 or 0 -- 对角线设为障碍
end
end
print(map2][2]) -- 输出: 1 (中心障碍)
2 ) 案例 2:角色属性配置(字典 + 元表)
需求:加载角色默认属性,支持自定义覆盖
local defaults = { hp = 100, mp = 50, speed = 10 }
local hero = setmetatable({ hp = 120 }, { index = defaults })
print(hero.mp) -- 输出: 50 (继承默认值)
hero.speed = 15 -- 自定义覆盖 [9]
3 ) 案例 3:物品合成系统(混合结构)
需求:验证玩家背包材料是否满足合成配方
local recipe = {
materials = { "wood", "iron", "iron" }, -- 数组:所需材料列表
quantities = { wood = 1, iron = 2 } -- 字典:材料数量要求
}
function canCraft(playerInventory)
for item, need in pairs(recipe.quantities) do
if (playerInventory[item] or 0) < need then
return false
end
end
return true
end
性能优化技巧
1 ) 数组预分配:避免动态扩容开销,提前初始化大小
local data = {}
for i = 1, 1000 do datai] = 0 end -- 一次性分配
2 ) 避免哈希冲突:对高频字典使用 string 而非 number 键,减少哈希计算开销
-- 原始方案:数字键 → 冲突风险高
local userDataByNumber = {}
for i = 1, 10000 do
userDataByNumber[i] = { name = "User" .. i } -- 数字键
end
-- 高频访问测试(易引发冲突)
for i = 1, 1000000 do
local key = math.random(1, 10000) -- 随机键
local data = userDataByNumber[key] -- 触发哈希计算
end
-- ✅ 优化方案:字符串键 → 减少冲突
local userDataByString = {}
for i = 1, 10000 do
userDataByString[tostring(i)] = { name = "User" .. i } -- 显式转为字符串键
end
-- 高频访问测试(哈希计算开销更低)
for i = 1, 1000000 do
local key = tostring(math.random(1, 10000)) -- 字符串键
local data = userDataByString[key] -- 优先命中缓存哈希值
end
3 ) 复用 Table:缓存频繁创建的 table,减少 GC 压力
local cache = {}
function getTempTable()
if #cache > 0 then return table.remove(cache) end
return {}
end
高级技巧与陷阱规避
1 ) 稀疏数组处理:用 ipairs 遍历连续部分,pairs 遍历非连续部分
-- 创建稀疏数组:连续部分 + 非连续键值对
local sparseArray = {
"apple", -- [1] 连续部分
"banana", -- [2] 连续部分
"cherry", -- [3] 连续部分
[5] = "elderberry", -- 非连续数字键
[8] = "fig", -- 非连续数字键
color = "red", -- 非数字键
[true] = "boolean_key" -- 布尔类型键
}
-- 1. 用 ipairs 遍历连续部分 (索引 1 开始的连续整数键)
print("===== 连续部分遍历 (ipairs) =====")
for idx, value in ipairs(sparseArray) do
print(string.format("索引 %d: %s", idx, value))
end
-- 输出:
-- 索引 1: apple
-- 索引 2: banana
-- 索引 3: cherry
-- 2. 用 pairs 遍历非连续部分 (跳过连续整数键)
print("\n===== 非连续部分遍历 (pairs) =====")
local maxIndex = #sparseArray -- 获取连续部分最大索引 (3)
for key, value in pairs(sparseArray) do
-- 过滤连续部分:跳过 1~maxIndex 的整数键
if type(key) ~= "number"
or key < 1
or key > maxIndex
or math.floor(key) ~= key then
local keyType = type(key)
print(string.format("键[%s](%s): %s",
tostring(key), keyType, value))
end
end
-- 输出示例(顺序可能不同):
-- 键[5](number): elderberry
-- 键[8](number): fig
-- 键[color](string): red
-- 键[true](boolean): boolean_key
2 ) 安全空表判断:
function isEmpty(t)
return next(t) == nil -- 高效检测空表 [9]
end
3 ) 自定义迭代顺序:通过元表重写 pairs,实现按键名排序输出
-- 自定义迭代器函数:按键名排序遍历表
local function sorted_pairs(t, order)
local keys = {}
for k in pairs(t) do
table.insert(keys, k)
end
-- 使用自定义排序规则(默认字母升序)
table.sort(keys, order)
local i = 0
return function()
i = i + 1
local key = keys[i]
if key then
return key, t[key]
end
end
end
-- 创建示例表
local data = {
beta = 2,
alpha = 1,
gamma = 3,
delta = 4
}
-- 设置元表重写 __pairs 方法
setmetatable(data, {
__pairs = function(self)
return sorted_pairs(self, function(a, b)
return a < b -- 自定义排序规则:字母升序
end)
end
})
-- 测试:按键名字母顺序输出
print("按键名字母升序输出:")
for k, v in pairs(data) do
print(k, v)
end
-- 可选:使用自定义排序规则(如按键名字母长度)
print("\n按键名长度升序输出:")
for k, v in sorted_pairs(data, function(a, b)
return #a < #b -- 按字符串长度排序
end) do
print(k, v)
end
设计哲学启示
Lua 的 table 设计体现了 “简单即强大” 的理念:
- 单一结构覆盖数组、字典、类、模块等多重角色
- 经济性优先:拒绝为小众场景增加语法糖(如内置 OOP),鼓励用基础结构组合实现复杂需求
完整代码示例及性能测试工具可参考:
最佳实践
- 根据使用场景选择遍历方式,数组优先使用ipairs保证遍历顺序,字典必须使用pairs
- 数组内部尽量保持索引连续以兼容#长度运算符与table系数组方法
- 避免在同一个表中大量混合使用数组与字典逻辑,维护时不利于代码可读性与性能优化
- 字典排序前务必将键转为独立数组,利用table.sort完成排序后再遍历取值
如果你想深入了解元表对数组与字典的自定义操作,我可以补充相关实践案例与代码示例
要点回顾
- Lua 的 table 是实现 数组 和 字典 的唯一数据结构
- 通过数字键访问时,table 表现为数组;通过字符串或其他类型键访问时,table 表现为字典
- 选择合适的遍历函数:ipairs 用于连续数字索引的数组,pairs 用于所有键值对(包括字典和非连续数组)
- 数组索引默认从 1 开始,这是 Lua 的约定
2957

被折叠的 条评论
为什么被折叠?



