Lua数组和表
概述
在Lua中,表(table)是唯一的数据结构,它既可以用作数组,也可以用作字典(关联数组)。表是Lua最强大和灵活的数据类型,理解表的使用是掌握Lua编程的关键。
1. 表的基础概念
1.1 表的创建
lua
-- 创建空表
local empty_table = {}
-- 创建数组形式的表
local array = {1, 2, 3, 4, 5}
-- 创建字典形式的表
local dict = {
name = "Lua",
version = 5.4,
year = 2020
}
-- 混合形式的表
local mixed = {
"first", -- [1] = "first"
"second", -- [2] = "second"
name = "mixed", -- ["name"] = "mixed"
[10] = "tenth", -- [10] = "tenth"
[true] = "boolean_key" -- [true] = "boolean_key"
}
print(array[1]) -- 1
print(dict.name) -- Lua
print(mixed[1]) -- first
print(mixed.name) -- mixed
print(mixed[10]) -- tenth1.2 表的索引
lua
local t = {}
-- 数字索引
t[1] = "first"
t[2] = "second"
-- 字符串索引
t["name"] = "example"
t.age = 25 -- 等同于 t["age"] = 25
-- 其他类型作为索引
t[true] = "boolean key"
t[{}] = "table key"
t[print] = "function key"
-- 访问元素
print(t[1]) -- first
print(t["name"]) -- example
print(t.age) -- 25
print(t[true]) -- boolean key
-- 不存在的键返回nil
print(t["nonexistent"]) -- nil2. 数组操作
2.1 数组的创建和访问
lua
-- 创建数组
local fruits = {"apple", "banana", "cherry", "date"}
-- 访问数组元素(索引从1开始)
print(fruits[1]) -- apple
print(fruits[2]) -- banana
-- 数组长度
print(#fruits) -- 4
-- 遍历数组
for i = 1, #fruits do
print(i, fruits[i])
end
-- 使用ipairs遍历数组
for index, value in ipairs(fruits) do
print(index, value)
end2.2 数组操作函数
lua
local numbers = {1, 2, 3, 4, 5}
-- table.insert - 插入元素
table.insert(numbers, 6) -- 在末尾插入
table.insert(numbers, 1, 0) -- 在位置1插入0
print(table.concat(numbers, ", ")) -- 0, 1, 2, 3, 4, 5, 6
-- table.remove - 删除元素
local removed = table.remove(numbers) -- 删除最后一个元素
print(removed) -- 6
local first = table.remove(numbers, 1) -- 删除第一个元素
print(first) -- 0
print(table.concat(numbers, ", ")) -- 1, 2, 3, 4, 5
-- table.concat - 连接数组元素
local words = {"Hello", "Lua", "World"}
local sentence = table.concat(words, " ")
print(sentence) -- Hello Lua World
-- table.sort - 排序数组
local unsorted = {5, 2, 8, 1, 9, 3}
table.sort(unsorted)
print(table.concat(unsorted, ", ")) -- 1, 2, 3, 5, 8, 9
-- 自定义排序
local people = {
{name = "Alice", age = 30},
{name = "Bob", age = 25},
{name = "Charlie", age = 35}
}
table.sort(people, function(a, b)
return a.age < b.age
end)
for _, person in ipairs(people) do
print(person.name, person.age)
end2.3 多维数组
lua
-- 创建二维数组
local matrix = {}
for i = 1, 3 do
matrix[i] = {}
for j = 1, 3 do
matrix[i][j] = i * j
end
end
-- 访问二维数组
for i = 1, 3 do
for j = 1, 3 do
io.write(matrix[i][j] .. " ")
end
print()
end
-- 直接初始化二维数组
local grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
-- 计算矩阵转置
local function transpose(matrix)
local result = {}
local rows = #matrix
local cols = #matrix[1]
for i = 1, cols do
result[i] = {}
for j = 1, rows do
result[i][j] = matrix[j][i]
end
end
return result
end
local transposed = transpose(grid)
print("原矩阵:")
for i = 1, #grid do
print(table.concat(grid[i], " "))
end
print("转置矩阵:")
for i = 1, #transposed do
print(table.concat(transposed[i], " "))
end3. 字典操作
3.1 字典的创建和使用
lua
-- 创建字典
local person = {
name = "Alice",
age = 30,
city = "Beijing",
hobbies = {"reading", "swimming", "coding"}
}
-- 访问字典元素
print(person.name) -- Alice
print(person["age"]) -- 30
-- 添加新键值对
person.job = "Engineer"
person["salary"] = 50000
-- 修改现有值
person.age = 31
-- 删除键值对
person.salary = nil
-- 检查键是否存在
if person.name then
print("Name exists: " .. person.name)
end
-- 遍历字典
for key, value in pairs(person) do
if type(value) == "table" then
print(key .. ": " .. table.concat(value, ", "))
else
print(key .. ": " .. tostring(value))
end
end3.2 嵌套字典
lua
-- 创建嵌套字典
local config = {
database = {
host = "localhost",
port = 3306,
credentials = {
username = "admin",
password = "secret"
}
},
server = {
port = 8080,
ssl = {
enabled = true,
cert_path = "/path/to/cert.pem"
}
}
}
-- 访问嵌套值
print(config.database.host) -- localhost
print(config.database.credentials.username) -- admin
print(config.server.ssl.enabled) -- true
-- 安全访问嵌套值
local function safe_get(table, ...)
local keys = {...}
local current = table
for _, key in ipairs(keys) do
if type(current) ~= "table" or current[key] == nil then
return nil
end
current = current[key]
end
return current
end
print(safe_get(config, "database", "host")) -- localhost
print(safe_get(config, "database", "nonexistent")) -- nil
print(safe_get(config, "nonexistent", "key")) -- nil4. 表的高级操作
4.1 表的复制
lua
-- 浅复制
local function shallow_copy(original)
local copy = {}
for key, value in pairs(original) do
copy[key] = value
end
return copy
end
-- 深复制
local function deep_copy(original)
local copy = {}
for key, value in pairs(original) do
if type(value) == "table" then
copy[key] = deep_copy(value)
else
copy[key] = value
end
end
return copy
end
local original = {
name = "test",
numbers = {1, 2, 3},
nested = {
value = 42
}
}
local shallow = shallow_copy(original)
local deep = deep_copy(original)
-- 修改原表的嵌套内容
original.nested.value = 100
print(original.nested.value) -- 100
print(shallow.nested.value) -- 100 (浅复制,共享引用)
print(deep.nested.value) -- 42 (深复制,独立副本)4.2 表的合并
lua
-- 合并两个表
local function merge_tables(t1, t2)
local result = {}
-- 复制第一个表
for key, value in pairs(t1) do
result[key] = value
end
-- 复制第二个表(会覆盖重复的键)
for key, value in pairs(t2) do
result[key] = value
end
return result
end
-- 深度合并
local function deep_merge(t1, t2)
local result = deep_copy(t1)
for key, value in pairs(t2) do
if type(value) == "table" and type(result[key]) == "table" then
result[key] = deep_merge(result[key], value)
else
result[key] = value
end
end
return result
end
local table1 = {
name = "config1",
database = {
host = "localhost",
port = 3306
}
}
local table2 = {
version = "1.0",
database = {
username = "admin",
password = "secret"
}
}
local merged = deep_merge(table1, table2)
for key, value in pairs(merged) do
if type(value) == "table" then
print(key .. ":")
for k, v in pairs(value) do
print(" " .. k .. ": " .. tostring(v))
end
else
print(key .. ": " .. tostring(value))
end
end4.3 表的序列化
lua
-- 简单的表序列化
local function serialize_table(t, indent)
indent = indent or 0
local spaces = string.rep(" ", indent)
local result = "{\n"
for key, value in pairs(t) do
local key_str
if type(key) == "string" then
key_str = string.format('["%s"]', key)
else
key_str = "[" .. tostring(key) .. "]"
end
local value_str
if type(value) == "table" then
value_str = serialize_table(value, indent + 1)
elseif type(value) == "string" then
value_str = string.format('"%s"', value)
else
value_str = tostring(value)
end
result = result .. spaces .. " " .. key_str .. " = " .. value_str .. ",\n"
end
result = result .. spaces .. "}"
return result
end
local data = {
name = "example",
version = 1.0,
features = {"feature1", "feature2"},
config = {
debug = true,
timeout = 30
}
}
print(serialize_table(data))5. 表作为数据结构
5.1 栈实现
lua
-- 使用表实现栈
local Stack = {}
Stack.__index = Stack
function Stack.new()
return setmetatable({}, Stack)
end
function Stack:push(item)
table.insert(self, item)
end
function Stack:pop()
if #self == 0 then
error("栈为空")
end
return table.remove(self)
end
function Stack:peek()
if #self == 0 then
return nil
end
return self[#self]
end
function Stack:is_empty()
return #self == 0
end
function Stack:size()
return #self
end
-- 使用栈
local stack = Stack.new()
stack:push(1)
stack:push(2)
stack:push(3)
print(stack:peek()) -- 3
print(stack:pop()) -- 3
print(stack:size()) -- 25.2 队列实现
lua
-- 使用表实现队列
local Queue = {}
Queue.__index = Queue
function Queue.new()
return setmetatable({first = 1, last = 0}, Queue)
end
function Queue:enqueue(item)
self.last = self.last + 1
self[self.last] = item
end
function Queue:dequeue()
if self.first > self.last then
error("队列为空")
end
local item = self[self.first]
self[self.first] = nil
self.first = self.first + 1
return item
end
function Queue:is_empty()
return self.first > self.last
end
function Queue:size()
return self.last - self.first + 1
end
-- 使用队列
local queue = Queue.new()
queue:enqueue("first")
queue:enqueue("second")
queue:enqueue("third")
print(queue:dequeue()) -- first
print(queue:dequeue()) -- second
print(queue:size()) -- 15.3 集合实现
lua
-- 使用表实现集合
local Set = {}
Set.__index = Set
function Set.new(items)
local set = setmetatable({}, Set)
if items then
for _, item in ipairs(items) do
set:add(item)
end
end
return set
end
function Set:add(item)
self[item] = true
end
function Set:remove(item)
self[item] = nil
end
function Set:contains(item)
return self[item] == true
end
function Set:size()
local count = 0
for _ in pairs(self) do
count = count + 1
end
return count
end
function Set:to_array()
local array = {}
for item in pairs(self) do
table.insert(array, item)
end
return array
end
function Set:union(other)
local result = Set.new()
for item in pairs(self) do
result:add(item)
end
for item in pairs(other) do
result:add(item)
end
return result
end
function Set:intersection(other)
local result = Set.new()
for item in pairs(self) do
if other:contains(item) then
result:add(item)
end
end
return result
end
-- 使用集合
local set1 = Set.new({1, 2, 3, 4})
local set2 = Set.new({3, 4, 5, 6})
print(set1:contains(2)) -- true
print(set1:contains(5)) -- false
local union = set1:union(set2)
local intersection = set1:intersection(set2)
print("并集:", table.concat(union:to_array(), ", "))
print("交集:", table.concat(intersection:to_array(), ", "))6. 表的性能优化
6.1 预分配表大小
lua
-- 低效:动态增长
local function create_large_array_slow(n)
local array = {}
for i = 1, n do
array[i] = i
end
return array
end
-- 高效:预分配
local function create_large_array_fast(n)
local array = {}
-- 预分配数组部分
for i = 1, n do
array[i] = false -- 占位符
end
-- 填充实际值
for i = 1, n do
array[i] = i
end
return array
end
-- 性能测试
local function time_function(func, ...)
local start = os.clock()
local result = func(...)
local finish = os.clock()
return result, finish - start
end
local n = 100000
local _, slow_time = time_function(create_large_array_slow, n)
local _, fast_time = time_function(create_large_array_fast, n)
print(string.format("动态增长: %.4f 秒", slow_time))
print(string.format("预分配: %.4f 秒", fast_time))6.2 避免表的重复查找
lua
-- 低效:重复查找
local function process_config_slow(config)
if config.database.connection.pool.max_size > 10 then
config.database.connection.pool.max_size = 10
end
if config.database.connection.pool.min_size < 1 then
config.database.connection.pool.min_size = 1
end
return config.database.connection.pool.max_size + config.database.connection.pool.min_size
end
-- 高效:缓存查找结果
local function process_config_fast(config)
local pool = config.database.connection.pool
if pool.max_size > 10 then
pool.max_size = 10
end
if pool.min_size < 1 then
pool.min_size = 1
end
return pool.max_size + pool.min_size
end7. 实际应用示例
7.1 数据缓存
lua
-- 简单的LRU缓存实现
local LRUCache = {}
LRUCache.__index = LRUCache
function LRUCache.new(max_size)
return setmetatable({
max_size = max_size,
data = {},
order = {},
size = 0
}, LRUCache)
end
function LRUCache:get(key)
local value = self.data[key]
if value then
-- 移动到最前面
self:_move_to_front(key)
return value
end
return nil
end
function LRUCache:set(key, value)
if self.data[key] then
-- 更新现有键
self.data[key] = value
self:_move_to_front(key)
else
-- 添加新键
if self.size >= self.max_size then
self:_evict_oldest()
end
self.data[key] = value
table.insert(self.order, 1, key)
self.size = self.size + 1
end
end
function LRUCache:_move_to_front(key)
for i, k in ipairs(self.order) do
if k == key then
table.remove(self.order, i)
table.insert(self.order, 1, key)
break
end
end
end
function LRUCache:_evict_oldest()
local oldest_key = table.remove(self.order)
self.data[oldest_key] = nil
self.size = self.size - 1
end
-- 使用缓存
local cache = LRUCache.new(3)
cache:set("a", 1)
cache:set("b", 2)
cache:set("c", 3)
cache:set("d", 4) -- 会淘汰 "a"
print(cache:get("a")) -- nil
print(cache:get("b")) -- 27.2 配置管理系统
lua
-- 配置管理系统
local ConfigManager = {}
ConfigManager.__index = ConfigManager
function ConfigManager.new()
return setmetatable({
configs = {},
defaults = {},
validators = {}
}, ConfigManager)
end
function ConfigManager:set_default(key, value)
self.defaults[key] = value
end
function ConfigManager:set_validator(key, validator)
self.validators[key] = validator
end
function ConfigManager:set(key, value)
-- 验证值
if self.validators[key] and not self.validators[key](value) then
error("Invalid value for " .. key)
end
self.configs[key] = value
end
function ConfigManager:get(key)
if self.configs[key] ~= nil then
return self.configs[key]
end
return self.defaults[key]
end
function ConfigManager:load_from_table(config_table)
for key, value in pairs(config_table) do
self:set(key, value)
end
end
function ConfigManager:get_all()
local result = {}
-- 添加默认值
for key, value in pairs(self.defaults) do
result[key] = value
end
-- 覆盖配置值
for key, value in pairs(self.configs) do
result[key] = value
end
return result
end
-- 使用配置管理器
local config = ConfigManager.new()
-- 设置默认值
config:set_default("host", "localhost")
config:set_default("port", 8080)
config:set_default("debug", false)
-- 设置验证器
config:set_validator("port", function(value)
return type(value) == "number" and value > 0 and value < 65536
end)
-- 加载配置
config:load_from_table({
host = "example.com",
port = 3000,
debug = true
})
print("Host:", config:get("host")) -- example.com
print("Port:", config:get("port")) -- 3000
print("Debug:", config:get("debug")) -- true8. 常见陷阱和注意事项
8.1 数组空洞问题
lua
-- 创建有空洞的数组
local sparse_array = {1, 2, nil, 4, 5}
print(#sparse_array) -- 结果不确定,可能是2或5
-- 正确处理稀疏数组
local function get_actual_length(array)
local max_index = 0
for i in pairs(array) do
if type(i) == "number" and i > max_index then
max_index = i
end
end
return max_index
end
local function count_elements(array)
local count = 0
for _ in pairs(array) do
count = count + 1
end
return count
end
print("最大索引:", get_actual_length(sparse_array))
print("元素个数:", count_elements(sparse_array))8.2 表的引用问题
lua
-- 表是引用类型
local original = {1, 2, 3}
local reference = original -- 引用,不是复制
reference[1] = 100
print(original[1]) -- 100,原表也被修改了
-- 正确的复制方法
local copy = {}
for i, v in ipairs(original) do
copy[i] = v
end
copy[2] = 200
print(original[2]) -- 2,原表不受影响
print(copy[2]) -- 2008.3 遍历时修改表
lua
-- 危险:在遍历时修改表
local t = {a = 1, b = 2, c = 3, d = 4}
-- 错误的做法
for key, value in pairs(t) do
if value % 2 == 0 then
t[key] = nil -- 可能导致遍历跳过某些元素
end
end
-- 正确的做法:先收集要删除的键
local to_remove = {}
for key, value in pairs(t) do
if value % 2 == 0 then
table.insert(to_remove, key)
end
end
for _, key in ipairs(to_remove) do
t[key] = nil
end总结
Lua的表是一个强大而灵活的数据结构:
- 统一的数据结构 - 既可以作为数组也可以作为字典使用
- 动态特性 - 可以在运行时添加、删除和修改元素
- 高效实现 - 内部使用哈希表和数组的混合结构
- 丰富的操作 - 提供了完整的操作函数库
- 广泛应用 - 可以实现各种复杂的数据结构
掌握表的使用是Lua编程的核心技能,合理运用表可以写出高效、优雅的程序。