Table是Lua中唯一的数据结构,可以实现数组、字典、对象等功能。Table在Lua中非常强大,它本质上是一个关联数组,可以使用任何类型(除了nil)作为索引。
Lua的Table采用混合存储策略,同时包含数组和哈希表两部分。对于连续的整数索引(从1开始),Lua会使用数组部分存储,这样可以提供O(1)的访问速度。对于其他类型的键,Lua使用哈希表存储。这种设计使得Lua的Table在处理数组时非常高效,同时保持了灵活性。
-- 创建空表
local t1 = {}
local t2 = {1, 2, 3}
-- 数组(索引从1开始)
local arr = {"苹果", "香蕉", "橙子"}
print(arr[1]) -- 苹果
print(arr[2]) -- 香蕉
print(#arr) -- 3 (长度)
-- Table作为字典
local dict = {}
dict["name"] = "张三"
dict[1] = "第一个元素"
dict[true] = "布尔值"
dict[1.5] = "浮点数索引"
-- 遍历所有键值对
for k, v in pairs(dict) do
print(string.format("[%s] = %s", tostring(k), tostring(v)))
end
Lua的#操作符计算数组长度时,会找到一个边界索引i,使得t[i]不为nil而t[i+1]为nil。这意味着如果数组中有"空洞"(nil值),#操作符可能返回不可预期的结果。对于连续数组,#操作符是O(1)操作,但对于有空洞的数组,可能需要O(n)时间。
-- 创建数组
local numbers = {10, 20, 30, 40, 50}
-- 访问元素
print(numbers[1]) -- 10
print(numbers[5]) -- 50
-- 修改元素
numbers[3] = 35
print(numbers[3]) -- 35
-- 添加元素
numbers[6] = 60
table.insert(numbers, 70) -- 在末尾添加
table.insert(numbers, 1, 5) -- 在位置1插入5
-- 删除元素
table.remove(numbers) -- 删除最后一个
table.remove(numbers, 1) -- 删除位置1的元素
-- 遍历数组
for i = 1, #numbers do
print(i, numbers[i])
end
-- 使用ipairs遍历
for index, value in ipairs(numbers) do
print(index, value)
end
-- 数组长度计算的注意事项
local arr = {1, 2, nil, 4}
print(#arr) -- 结果可能是2或4,取决于Lua版本和实现
-- 安全的数组遍历方法
local function safeLength(arr)
local count = 0
for _ in pairs(arr) do
count = count + 1
end
return count
end
-- 数组切片函数
local function slice(arr, start, finish)
local result = {}
start = start or 1
finish = finish or #arr
for i = start, finish do
table.insert(result, arr[i])
end
return result
end
local fruits = {"苹果", "香蕉", "橙子", "葡萄", "西瓜"}
local sub = slice(fruits, 2, 4)
print(table.concat(sub, ", ")) -- 香蕉, 橙子, 葡萄
Lua使用开放寻址法实现哈希表。当发生哈希冲突时,Lua会按照某种策略(通常是线性探测)寻找下一个可用的槽位。Lua会自动调整哈希表的大小,当负载因子超过某个阈值时进行扩容。这种实现方式在大多数情况下都能提供良好的性能,但在极端情况下(大量哈希冲突)性能会下降。
-- 创建字典
local person = {
name = "张三",
age = 25,
city = "北京"
}
-- 访问元素
print(person.name) -- 张三
print(person["age"]) -- 25
-- 添加/修改元素
person.job = "工程师"
person["salary"] = 10000
-- 删除元素
person.city = nil
-- 遍历字典
for key, value in pairs(person) do
print(key, value)
end
-- 检查键是否存在
if person["name"] ~= nil then
print("name键存在")
end
-- 使用不同的键类型
local mixedDict = {
[1] = "整数键",
["string"] = "字符串键",
[true] = "布尔键",
[1.5] = "浮点数键",
[{x=1, y=2}] = "表键" -- 注意:每次创建新表都是不同的对象
}
-- 深度嵌套的字典
local config = {
database = {
host = "localhost",
port = 3306,
credentials = {
username = "admin",
password = "secret"
}
},
server = {
port = 8080,
ssl = true
}
}
print(config.database.credentials.username) -- admin
-- 字典合并函数
local function merge(t1, t2)
local result = {}
for k, v in pairs(t1) do
result[k] = v
end
for k, v in pairs(t2) do
result[k] = v
end
return result
end
local dict1 = {a = 1, b = 2}
local dict2 = {c = 3, d = 4}
local merged = merge(dict1, dict2)
for k, v in pairs(merged) do
print(k, v)
end
-- 同时包含数组和字典部分
local mixed = {
"第一个元素", -- [1]
"第二个元素", -- [2]
name = "混合表",
count = 100
}
print(mixed[1]) -- 第一个元素
print(mixed.name) -- 混合表
print(#mixed) -- 2 (只计算数组部分)
-- 遍历所有元素
for k, v in pairs(mixed) do
print(k, v)
end
table.insert和table.remove在数组末尾操作是O(1)时间复杂度,但在数组中间或开头操作是O(n)时间复杂度,因为需要移动元素。table.sort使用快速排序算法,平均时间复杂度为O(n log n)。table.concat在连接大量字符串时非常高效,因为它预先计算最终字符串大小,避免了多次内存分配。
local t = {3, 1, 4, 1, 5, 9, 2, 6}
-- table.insert(table, [pos,] value)
table.insert(t, 100) -- 末尾插入
table.insert(t, 1, 0) -- 位置1插入
-- table.remove(table, [pos])
table.remove(t) -- 删除最后一个
table.remove(t, 1) -- 删除位置1
-- table.sort(table, [comp])
table.sort(t) -- 升序排序
print(table.concat(t, ", "))
table.sort(t, function(a, b) return a > b end) -- 降序
print(table.concat(t, ", "))
-- table.concat(table, [sep, [i, [j]]])
local words = {"Hello", "Lua", "World"}
print(table.concat(words, " ")) -- Hello Lua World
-- table.unpack(table, [i, [j]])
local a, b, c = table.unpack({10, 20, 30})
print(a, b, c) -- 10 20 30
-- table.move(table, from, to, target, [dest_table])
local src = {1, 2, 3, 4, 5}
local dest = {0, 0, 0, 0, 0}
table.move(src, 2, 4, 3, dest)
print(table.concat(dest, ", ")) -- 0, 0, 2, 3, 4
-- table.pack(...)
local packed = table.pack("a", "b", "c", nil, "e")
print(packed.n) -- 5 (元素数量)
-- table.maxn(table) - 已弃用,使用#代替
local arr = {10, 20, 30, [100] = 100}
print(#arr) -- 3
-- 实用函数:数组去重
local function unique(arr)
local seen = {}
local result = {}
for i, v in ipairs(arr) do
if not seen[v] then
seen[v] = true
table.insert(result, v)
end
end
return result
end
local duplicates = {1, 2, 2, 3, 3, 3, 4}
print(table.concat(unique(duplicates), ", ")) -- 1, 2, 3, 4
-- 实用函数:数组反转
local function reverse(arr)
local result = {}
for i = #arr, 1, -1 do
table.insert(result, arr[i])
end
return result
end
local nums = {1, 2, 3, 4, 5}
print(table.concat(reverse(nums), ", ")) -- 5, 4, 3, 2, 1
-- 实用函数:数组分组
local function groupBy(arr, size)
local result = {}
for i = 1, #arr, size do
local group = {}
for j = i, math.min(i + size - 1, #arr) do
table.insert(group, arr[j])
end
table.insert(result, group)
end
return result
end
local data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local groups = groupBy(data, 3)
for i, group in ipairs(groups) do
print("Group " .. i .. ": " .. table.concat(group, ", "))
end
Lua中没有真正的多维数组,而是使用"数组的数组"来模拟。每个元素本身是一个数组。这种实现方式灵活但可能带来性能开销,因为每次访问都需要两次索引操作。对于大型矩阵,可以考虑使用一维数组配合索引计算来提高性能。
-- 创建二维数组
local matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
-- 访问元素
print(matrix[2][3]) -- 6
-- 遍历二维数组
for i = 1, #matrix do
for j = 1, #matrix[i] do
io.write(matrix[i][j] .. " ")
end
print()
end
-- 动态创建二维数组
local rows, cols = 3, 4
local arr = {}
for i = 1, rows do
arr[i] = {}
for j = 1, cols do
arr[i][j] = i * j
end
end
-- 矩阵转置
local function transpose(matrix)
local result = {}
for i = 1, #matrix[1] do
result[i] = {}
for j = 1, #matrix do
result[i][j] = matrix[j][i]
end
end
return result
end
local m = {{1, 2, 3}, {4, 5, 6}}
local tm = transpose(m)
for i = 1, #tm do
print(table.concat(tm[i], ", "))
end
-- 矩阵乘法
local function matrixMultiply(a, b)
local rowsA, colsA = #a, #a[1]
local rowsB, colsB = #b, #b[1]
if colsA ~= rowsB then
error("矩阵维度不匹配")
end
local result = {}
for i = 1, rowsA do
result[i] = {}
for j = 1, colsB do
local sum = 0
for k = 1, colsA do
sum = sum + a[i][k] * b[k][j]
end
result[i][j] = sum
end
end
return result
end
local mat1 = {{1, 2}, {3, 4}}
local mat2 = {{5, 6}, {7, 8}}
local product = matrixMultiply(mat1, mat2)
for i = 1, #product do
print(table.concat(product[i], ", "))
end
在Lua中,集合通常通过Table实现,将元素作为键,值设为true。这种实现方式利用了Lua的哈希表,提供了O(1)的平均插入、删除和查找时间复杂度。集合操作(并集、交集、差集)的时间复杂度为O(n),其中n是集合的大小。
-- 使用table实现集合
local set = {}
-- 添加元素
set["apple"] = true
set["banana"] = true
set["orange"] = true
-- 检查元素是否存在
if set["apple"] then
print("集合中有apple")
end
-- 删除元素
set["banana"] = nil
-- 遍历集合
for element in pairs(set) do
print(element)
end
-- 集合操作
function union(a, b)
local result = {}
for k in pairs(a) do result[k] = true end
for k in pairs(b) do result[k] = true end
return result
end
function intersection(a, b)
local result = {}
for k in pairs(a) do
if b[k] then result[k] = true end
end
return result
end
function difference(a, b)
local result = {}
for k in pairs(a) do
if not b[k] then result[k] = true end
end
return result
end
function isSubset(a, b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
local set1 = {a = true, b = true, c = true}
local set2 = {b = true, c = true, d = true}
print("并集:")
for k in pairs(union(set1, set2)) do print(k) end
print("交集:")
for k in pairs(intersection(set1, set2)) do print(k) end
print("差集 (set1 - set2):")
for k in pairs(difference(set1, set2)) do print(k) end
print("set1是set2的子集吗?", isSubset(set1, set2))
-- 有序集合(使用数组+table)
local OrderedSet = {}
OrderedSet.__index = OrderedSet
function OrderedSet.new()
return setmetatable({
items = {},
lookup = {}
}, OrderedSet)
end
function OrderedSet:add(item)
if not self.lookup[item] then
table.insert(self.items, item)
self.lookup[item] = #self.items
end
end
function OrderedSet:remove(item)
local index = self.lookup[item]
if index then
table.remove(self.items, index)
self.lookup[item] = nil
-- 更新后续元素的索引
for i = index, #self.items do
self.lookup[self.items[i]] = i
end
end
end
function OrderedSet:contains(item)
return self.lookup[item] ~= nil
end
function OrderedSet:iterate()
local i = 0
return function()
i = i + 1
return self.items[i]
end
end
local oset = OrderedSet.new()
oset:add("first")
oset:add("second")
oset:add("third")
oset:add("first") -- 不会重复添加
for item in oset:iterate() do
print(item)
end
弱引用表允许垃圾回收器回收表中的键或值,即使它们仍然被弱引用表引用。通过设置表的__mode元字段为"k"、"v"或"kv",可以分别使键、值或两者都成为弱引用。这在实现缓存、对象池等场景中非常有用。
-- 弱引用表示例
local weakTable = setmetatable({}, {__mode = "v"})
local key = {}
weakTable[key] = "value"
print(weakTable[key]) -- value
-- 删除对value的强引用
value = nil
-- 强制垃圾回收
collectgarbage()
-- 值已被回收
print(weakTable[key]) -- nil
-- 对象池实现
local ObjectPool = {}
ObjectPool.__index = ObjectPool
function ObjectPool.new()
return setmetatable({
pool = setmetatable({}, {__mode = "v"}),
factory = function() return {} end
}, ObjectPool)
end
function ObjectPool:acquire()
local obj = table.remove(self.pool)
if not obj then
obj = self.factory()
end
return obj
end
function ObjectPool:release(obj)
table.insert(self.pool, obj)
end
-- 缓存实现
local memoize = function(func)
local cache = setmetatable({}, {__mode = "k"})
return function(x)
local r = cache[x]
if not r then
r = func(x)
cache[x] = r
end
return r
end
end
local slowFib = function(n)
if n < 2 then return n end
return slowFib(n - 1) + slowFib(n - 2)
end
local fastFib = memoize(slowFib)
print(fastFib(35)) -- 计算很快,因为使用了缓存
-- 简单的Table序列化
local function serialize(t)
local result = {}
local function helper(t, indent)
indent = indent or ""
for k, v in pairs(t) do
local key = type(k) == "string" and k or "[" .. tostring(k) .. "]"
if type(v) == "table" then
table.insert(result, indent .. key .. " = {")
helper(v, indent .. " ")
table.insert(result, indent .. "},")
else
local value = type(v) == "string" and '"' .. v .. '"' or tostring(v)
table.insert(result, indent .. key .. " = " .. value .. ",")
end
end
end
helper(t)
return table.concat(result, "\n")
end
local data = {
name = "张三",
age = 25,
scores = {90, 85, 95},
address = {
city = "北京",
street = "长安街"
}
}
print(serialize(data))
-- Table比较(深度比较)
local function deepEqual(t1, t2)
if t1 == t2 then return true end
if type(t1) ~= "table" or type(t2) ~= "table" then return false end
for k in pairs(t1) do
if not deepEqual(t1[k], t2[k]) then return false end
end
for k in pairs(t2) do
if t1[k] == nil then return false end
end
return true
end
local t1 = {a = 1, b = {c = 2}}
local t2 = {a = 1, b = {c = 2}}
local t3 = {a = 1, b = {c = 3}}
print(deepEqual(t1, t2)) -- true
print(deepEqual(t1, t3)) -- false
Table是引用类型,赋值时传递的是引用而非副本。这意味着多个变量可以指向同一个Table,修改其中一个会影响所有引用。理解这一点对于避免意外的副作用非常重要。在需要独立副本时,必须显式进行浅拷贝或深拷贝。
-- Table是引用类型
local t1 = {1, 2, 3}
local t2 = t1 -- t2引用t1
t2[1] = 100
print(t1[1]) -- 100 (t1也被修改了)
-- 复制table(浅拷贝)
function shallowCopy(original)
local copy = {}
for k, v in pairs(original) do
copy[k] = v
end
return copy
end
local t3 = shallowCopy(t1)
t3[1] = 200
print(t1[1]) -- 100 (t1不受影响)
-- 深拷贝
function deepCopy(original)
local copy
if type(original) == "table" then
copy = {}
for k, v in pairs(original) do
copy[deepCopy(k)] = deepCopy(v)
end
else
copy = original
end
return copy
end
-- 测试深拷贝
local original = {
a = 1,
b = {c = 2, d = {e = 3}}
}
local shallow = shallowCopy(original)
shallow.b.c = 100
print(original.b.c) -- 100 (原对象被修改)
local deep = deepCopy(original)
deep.b.d.e = 200
print(original.b.d.e) -- 3 (原对象不受影响)
-- 函数参数中的Table
function modifyTable(t)
t.newKey = "new value"
end
local myTable = {oldKey = "old value"}
modifyTable(myTable)
print(myTable.newKey) -- new value
-- 避免意外修改的技巧
function safeModify(t)
local copy = deepCopy(t)
copy.newKey = "new value"
return copy
end
local safeTable = safeModify(myTable)
print(myTable.newKey) -- new value (原表仍被修改,因为myTable本身就是引用)
-- 简单的内存数据库系统
local SimpleDB = {}
SimpleDB.__index = SimpleDB
function SimpleDB.new()
return setmetatable({
tables = {},
indexes = {}
}, SimpleDB)
end
function SimpleDB:createTable(name, schema)
self.tables[name] = {
schema = schema,
data = {},
autoIncrement = 1
}
self.indexes[name] = {}
for field in pairs(schema) do
self.indexes[name][field] = {}
end
end
function SimpleDB:insert(tableName, record)
local table = self.tables[tableName]
if not table then
error("Table '" .. tableName .. "' does not exist")
end
local id = table.autoIncrement
record.id = id
table.data[id] = record
-- 更新索引
for field in pairs(table.schema) do
if record[field] then
local index = self.indexes[tableName][field]
local key = tostring(record[field])
if not index[key] then
index[key] = {}
end
table.insert(index[key], id)
end
end
table.autoIncrement = table.autoIncrement + 1
return id
end
function SimpleDB:select(tableName, conditions)
local table = self.tables[tableName]
if not table then
error("Table '" .. tableName .. "' does not exist")
end
local results = {}
if not conditions or next(conditions) == nil then
-- 返回所有记录
for id, record in pairs(table.data) do
table.insert(results, record)
end
else
-- 使用索引优化查询
local candidateIds = nil
for field, value in pairs(conditions) do
local index = self.indexes[tableName][field]
local key = tostring(value)
if index[key] then
if not candidateIds then
candidateIds = {}
for _, id in ipairs(index[key]) do
candidateIds[id] = true
end
else
-- 取交集
local newCandidates = {}
for _, id in ipairs(index[key]) do
if candidateIds[id] then
newCandidates[id] = true
end
end
candidateIds = newCandidates
end
else
-- 没有匹配的记录
return {}
end
end
if candidateIds then
for id in pairs(candidateIds) do
local record = table.data[id]
local match = true
for field, value in pairs(conditions) do
if record[field] ~= value then
match = false
break
end
end
if match then
table.insert(results, record)
end
end
end
end
return results
end
function SimpleDB:update(tableName, conditions, updates)
local records = self:select(tableName, conditions)
for _, record in ipairs(records) do
for field, value in pairs(updates) do
record[field] = value
end
end
return #records
end
function SimpleDB:delete(tableName, conditions)
local table = self.tables[tableName]
local records = self:select(tableName, conditions)
local count = #records
for _, record in ipairs(records) do
local id = record.id
table.data[id] = nil
-- 从索引中删除
for field in pairs(table.schema) do
local index = self.indexes[tableName][field]
local key = tostring(record[field])
if index[key] then
for i, idxId in ipairs(index[key]) do
if idxId == id then
table.remove(index[key], i)
break
end
end
end
end
end
return count
end
-- 使用示例
local db = SimpleDB.new()
-- 创建表
db:createTable("users", {
name = "string",
age = "number",
email = "string"
})
-- 插入数据
local id1 = db:insert("users", {name = "张三", age = 25, email = "zhangsan@example.com"})
local id2 = db:insert("users", {name = "李四", age = 30, email = "lisi@example.com"})
local id3 = db:insert("users", {name = "王五", age = 25, email = "wangwu@example.com"})
-- 查询所有用户
print("所有用户:")
for _, user in ipairs(db:select("users")) do
print(string.format("%d: %s, %d岁, %s", user.id, user.name, user.age, user.email))
end
-- 条件查询
print("\n年龄为25岁的用户:")
for _, user in ipairs(db:select("users", {age = 25})) do
print(user.name)
end
-- 更新数据
db:update("users", {name = "张三"}, {age = 26})
-- 删除数据
db:delete("users", {name = "王五"})
print("\n更新后的用户:")
for _, user in ipairs(db:select("users")) do
print(string.format("%d: %s, %d岁", user.id, user.name, user.age))
end