Swift 字典
字典(Dictionary)是 Swift 的另一个核心集合类型——键值对映射。它相当于 Python 的 dict、Java 的 HashMap、JS 的 Object。配置项、用户信息、缓存——任何"用名字查数据"的场景都适合用字典。
1. 创建
// 字典:键值对,key 唯一,key 必须是 Hashable
var scores: [String: Int] = [
"小明": 90,
"小红": 85,
"小张": 78,
]
// 类型推断
var cache = ["a": 1, "b": 2] // [String: Int]
// 空字典
var empty1: [String: Int] = [:]
var empty2 = [String: Int]()
print(scores.count) // 3
print(scores.isEmpty) // false几个关键点:
- 键必须 Hashable:Swift 内置类型(String/Int/Double/Bool 等)都是 Hashable,可以直接当 key。自定义类型当 key 要遵守 Hashable 协议。
- 无序:字典不保证遍历顺序和插入顺序一致。要排序先 sort。
- 类型声明用
[KeyType: ValueType],如[String: Int]。
2. 查询(返回 Optional)
这是 Swift 字典和大多数语言最大的不同——取值返回 Optional,因为 key 可能不存在:
// 查询:返回 Optional(键不存在时返回 nil)
let scores = ["小明": 90, "小红": 85]
print(scores["小明"] ?? -1) // 90
print(scores["小张"] ?? -1) // -1(不存在)
// 强制解包(只在确定存在时用)
// print(scores["小明"]!) // 90,但不存在会崩溃
// 默认值:用 ?? 或下标 default
let score = scores["小张", default: 60]
print(score) // 60(不存在时给默认)
// 包含某个 key
if scores.keys.contains("小明") {
print("有小明")
}
// 更优雅:
if scores["小明"] != nil {
print("有小明")
}这条设计避免了"key 不存在时返回 null/undefined,后续操作全崩"的经典 bug。配合 ?? 给默认值,代码既安全又简洁。
3. 增删改
var scores: [String: Int] = ["小明": 90]
// 新增/修改(同一个语法)
scores["小红"] = 85 // 新增
scores["小明"] = 95 // 修改(已存在)
print(scores)
// updateValue:返回旧值(Optional)
if let old = scores.updateValue(100, forKey: "小明") {
print("小明的旧分: \(old)") // 95
}
// 删除
scores["小张"] = 70
scores["小张"] = nil // 设为 nil 等于删除
print(scores)
// removeValue:返回被删的值
if let removed = scores.removeValue(forKey: "小红") {
print("删除了 \(removed)")
}
// 清空
scores.removeAll()新增和修改用同一个语法(dict[key] = value)——key 不存在就新增,存在就覆盖。删除有两种:设为 nil 或 removeValue(forKey:)。后者返回被删的值。
4. 遍历
let scores = ["小明": 90, "小红": 85, "小张": 78]
// 遍历键值对(顺序不保证!字典无序)
for (name, score) in scores {
print("\(name): \(score)")
}
// 只遍历 key
for name in scores.keys {
print(name)
}
// 只遍历 value
for score in scores.values {
print(score)
}
// 想要排序的遍历,先排序
for name in scores.keys.sorted() {
print("\(name): \(scores[name]!)")
}
// 转数组
let allKeys = Array(scores.keys)
let allValues = Array(scores.values)遍历解构 (key, value) 是最常用模式。注意顺序不保证,需要排序先 sorted()。.keys 和 .values 是懒计算集合,转 Array 才能多次遍历。
5. 分组与 mapValues(超实用)
Swift 标准库提供了几个超实用的字典构造工具:
// group:按某条件把数组分组
let students = [
("小明", "一班"), ("小红", "一班"),
("小张", "二班"), ("小李", "二班"), ("小王", "三班"),
]
// 按班级分组
let byClass = Dictionary(grouping: students, by: { $0.1 })
print(byClass)
// ["一班": [(小明, 一班), (小红, 一班)],
// "二班": [(小张, 二班), (小李, 二班)],
// "三班": [(小王, 三班)]]
// 统计每个班级人数
let counts = Dictionary(grouping: students, by: { $0.1 })
.mapValues { $0.count }
print(counts) // ["一班": 2, "二班": 2, "三班": 1]
// mapValues:只变换 value,保留 key
let doubled = ["a": 1, "b": 2].mapValues { $0 * 2 }
print(doubled) // ["a": 2, "b": 4]Dictionary(grouping:by:) 是处理"分类"问题的神器——按班级分组、按日期分组、按状态分组,一行搞定。mapValues 只变换值保留键,常用于"修改配置但不改 key"。
6. 与 JSON 互转
字典和 JSON 是天然的一对——键值对结构完全对应。Swift 标准库提供 JSONSerialization 做基本转换:
import Foundation
// 字典 → JSON
let user: [String: Any] = [
"name": "小明",
"age": 18,
"isVip": true,
]
if let data = try? JSONSerialization.data(withJSONObject: user, options: .prettyPrinted),
let json = String(data: data, encoding: .utf8) {
print(json)
}
// JSON → 字典
let jsonString = """
{"name": "小红", "age": 20}
"""
let data = jsonString.data(using: .utf8)!
if let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
print(dict["name"] ?? "") // 小红
}
// 实战推荐:用 Codable 协议把 JSON 解成 struct,后面专章讲但实战中更推荐用 Codable 协议——把 JSON 直接解码成 struct,类型安全、自动处理可选字段、代码极简。这在后续"进阶"章会讲。
7. 性能特点
- 查询/插入/删除:平均 O(1),最坏 O(n)(哈希冲突)。
- 遍历:O(n)。
- 哈希开销:String 作 key 有计算哈希的成本,超高频访问场景可考虑用 Int 作 key。
- 有序字典:标准库没有,需要第三方库或用 [(K, V)] 数组保序。
8. 常见陷阱
- 返回 Optional:忘记解包就当 Int 用,编译错误。这是好事,强迫你处理"key 不存在"。
- key 类型:不能用 Array / Dictionary 当 key(它们不是 Hashable 的元素类型);自定义类型要实现 Hashable。
- 遍历中修改:遍历字典时插入/删除会触发运行时错误。先收集改动,遍历完再统一改。
- 顺序:不要依赖遍历顺序做"取第一个"——结果可能出乎意料。
小结
字典 = 键值对 + Optional 返回 + 增删改查 + grouping/mapValues + JSON 互转。和数组搭配,日常 90% 的数据组织都能搞定。下一篇讲 Swift 的类型系统核心——类与结构体。
← 上一篇 Swift 数组
下一篇 Swift 类与结构体 →