Skip to content

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])     -- tenth

1.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"])  -- nil

2. 数组操作

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)
end

2.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)
end

2.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], " "))
end

3. 字典操作

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
end

3.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"))         -- nil

4. 表的高级操作

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
end

4.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())     -- 2

5.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())     -- 1

5.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
end

7. 实际应用示例

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"))  -- 2

7.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"))  -- true

8. 常见陷阱和注意事项

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])      -- 200

8.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的表是一个强大而灵活的数据结构:

  1. 统一的数据结构 - 既可以作为数组也可以作为字典使用
  2. 动态特性 - 可以在运行时添加、删除和修改元素
  3. 高效实现 - 内部使用哈希表和数组的混合结构
  4. 丰富的操作 - 提供了完整的操作函数库
  5. 广泛应用 - 可以实现各种复杂的数据结构

掌握表的使用是Lua编程的核心技能,合理运用表可以写出高效、优雅的程序。

基于 MIT 许可发布