第7章 - Table(表)
嗨,朋友!我是长安。
Table 是 Lua 中最重要、最核心的数据结构——没有之一!在 Java 中你有 Array、List、Map、Set、Object……但在 Lua 中,这些全部由 Table 一个数据结构搞定。
🤔 Table 是什么?
简单来说,Table 就是一个关联数组,它可以用任何值(除了 nil)作为键,也可以存储任何值。
| Java 数据结构 | Lua 实现方式 |
|---|---|
int[] 数组 | Table(整数索引) |
ArrayList | Table(整数索引) |
HashMap | Table(键值对) |
Object 对象 | Table(键值对) |
HashSet | Table(值作为键) |
📦 创建 Table
-- 空表
local t = {}
-- 数组风格(索引从 1 开始!不是 0!)
local fruits = {"苹果", "香蕉", "橘子"}
print(fruits[1]) -- 苹果(不是 fruits[0]!)
print(fruits[2]) -- 香蕉
print(fruits[3]) -- 橘子
-- 字典风格
local person = {
name = "长安",
age = 25,
job = "Java开发"
}
print(person.name) -- 长安
print(person["age"]) -- 25(另一种访问方式)
Java 程序员最大的坑
Lua 的数组索引从 1 开始,不是 0! 这是 Java 程序员最容易犯错的地方。
local arr = {"a", "b", "c"}
print(arr[0]) -- nil(不是 "a"!)
print(arr[1]) -- a(这才是第一个元素)
📋 Table 作为数组
local numbers = {10, 20, 30, 40, 50}
-- 获取长度
print(#numbers) -- 5
-- 遍历数组(用 ipairs)
for i, v in ipairs(numbers) do
print(i, v)
end
-- 1 10
-- 2 20
-- 3 30
-- 4 40
-- 5 50
-- 添加元素
table.insert(numbers, 60) -- 在末尾添加
table.insert(numbers, 1, 5) -- 在位置 1 插入
-- 删除元素
table.remove(numbers, 1) -- 删除位置 1 的元素
local last = table.remove(numbers) -- 删除并返回最后一个元素
-- 排序
table.sort(numbers) -- 升序排序
table.sort(numbers, function(a, b) -- 降序排序
return a > b
end)
// Java 等价操作
List<Integer> numbers = new ArrayList<>(Arrays.asList(10, 20, 30, 40, 50));
// 获取长度
System.out.println(numbers.size()); // 5
// 遍历
for (int i = 0; i < numbers.size(); i++) {
System.out.println((i+1) + " " + numbers.get(i));
}
// 添加/删除
numbers.add(60);
numbers.add(0, 5);
numbers.remove(0);
// 排序
Collections.sort(numbers);
Collections.sort(numbers, Collections.reverseOrder());
📖 Table 作为字典(Map)
local config = {
host = "localhost",
port = 8080,
debug = true
}
-- 访问值(两种方式)
print(config.host) -- localhost
print(config["port"]) -- 8080
-- 修改值
config.port = 9090
-- 添加新键值对
config.timeout = 3000
-- 删除键值对(设置为 nil)
config.debug = nil
-- 遍历所有键值对(用 pairs)
for key, value in pairs(config) do
print(key .. " = " .. tostring(value))
end
-- 检查键是否存在
if config.host then
print("host 存在: " .. config.host)
end
// Java 等价操作
Map<String, Object> config = new HashMap<>();
config.put("host", "localhost");
config.put("port", 8080);
config.put("debug", true);
// 访问
System.out.println(config.get("host"));
// 修改 / 添加 / 删除
config.put("port", 9090);
config.put("timeout", 3000);
config.remove("debug");
// 遍历
for (Map.Entry<String, Object> entry : config.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 检查
if (config.containsKey("host")) {
System.out.println("host 存在: " + config.get("host"));
}
🏗️ Table 作为对象
-- 用 Table 模拟一个"对象"
local player = {
name = "战士",
hp = 100,
attack = 25,
-- 方法:注意用冒号定义(自动传入 self)
takeDamage = function(self, damage)
self.hp = self.hp - damage
print(self.name .. " 受到 " .. damage .. " 点伤害,剩余 HP: " .. self.hp)
end
}
-- 调用方法(用冒号调用,自动传入 self)
player:takeDamage(30) -- 战士 受到 30 点伤害,剩余 HP: 70
-- 等价于:
player.takeDamage(player, 30) -- 用点号需要手动传 self
冒号语法糖
Lua 中 : 是一个语法糖:
- 定义时
function obj:method()等价于function obj.method(self) - 调用时
obj:method()等价于obj.method(obj)
类似于 Java 中方法自动拥有 this 引用。
🔄 嵌套 Table
-- 嵌套结构(类似 Java 中对象嵌套)
local company = {
name = "科技公司",
employees = {
{name = "张三", age = 28, dept = "后端"},
{name = "李四", age = 25, dept = "前端"},
{name = "王五", age = 30, dept = "后端"},
},
address = {
city = "北京",
street = "中关村大街"
}
}
-- 访问嵌套数据
print(company.name) -- 科技公司
print(company.address.city) -- 北京
print(company.employees[1].name) -- 张三
print(company.employees[2].dept) -- 前端
-- 遍历员工
for i, emp in ipairs(company.employees) do
print(string.format("%d. %s (%s部门)", i, emp.name, emp.dept))
end
-- 1. 张三 (后端部门)
-- 2. 李四 (前端部门)
-- 3. 王五 (后端部门)
📊 Table 常用操作速查
local t = {1, 2, 3, 4, 5}
-- 长度
print(#t) -- 5
-- 插入
table.insert(t, 6) -- 末尾插入 6
table.insert(t, 1, 0) -- 位置 1 插入 0
-- 删除
table.remove(t, 1) -- 删除位置 1
table.remove(t) -- 删除最后一个
-- 拼接(数组元素拼接为字符串)
local arr = {"Hello", "World", "Lua"}
print(table.concat(arr, ", ")) -- Hello, World, Lua
-- 类似 Java 的 String.join(", ", arr)
-- 排序
table.sort(t) -- 升序
table.sort(t, function(a, b) return a > b end) -- 降序
-- 解包
local a, b, c = table.unpack({10, 20, 30})
print(a, b, c) -- 10 20 30
💡 Table 的引用特性
Table 是引用类型,赋值不会复制,而是共享同一个 Table:
local a = {1, 2, 3}
local b = a -- b 和 a 指向同一个 table!
b[1] = 100
print(a[1]) -- 100(a 也被改了!)
-- 如果要复制,需要手动遍历
local function shallowCopy(t)
local copy = {}
for k, v in pairs(t) do
copy[k] = v
end
return copy
end
local c = shallowCopy(a)
c[1] = 999
print(a[1]) -- 100(a 不受影响)
Java 程序员应该很熟悉
这和 Java 的引用类型完全一样!List<Integer> b = a; 也是引用赋值,修改 b 会影响 a。
📝 小结
- Table 是 Lua 唯一的数据结构,可以当数组、字典、对象用
- 数组索引从 1 开始,不是 0!
- 用
ipairs遍历数组,用pairs遍历字典 - 冒号
:是语法糖,自动传入self参数 - Table 是引用类型,赋值不会复制
#获取数组长度,table.insert/remove/sort操作数组
➡️ 下一步
Table 掌握了!下一章我们来学习 字符串与模式匹配,看看 Lua 处理字符串有多灵活。
💪 练习题
- 创建一个数组 Table,存储 5 个城市名称,打印第 3 个城市。
- 创建一个字典 Table 表示一本书(书名、作者、价格),打印所有信息。
- 用 Table 实现一个简单的"栈"(push 和 pop 操作)。
- Lua 数组索引从几开始?为什么这对 Java 程序员很重要?
答案提示
local cities = {"北京","上海","广州","深圳","杭州"}→print(cities[3])local book = {title="Lua入门", author="长安", price=59}+pairs遍历- push:
table.insert(stack, value), pop:table.remove(stack) - 从 1 开始。Java 从 0 开始,容易写成
arr[0]导致取到 nil
