Swift: Swift 集合与字典教程:Set 运算和 Dictionary 用法详解
集合就像一袋彩色弹珠——每个颜色只有一颗,但混在一起不分顺序。字典就像一本电话簿——按名字快速查到号码。本课带你掌握这两种强大的数据结构。
1. 你将学到
- 创建和操作 Set,掌握集合的交集、并集等运算
- 创建和操作 Dictionary,实现键值对存储
- 理解 Hashable 协议和自定义类型作为键
- 使用 Set 进行高效的去重和成员检测
- 遍历和修改 Dictionary 的常用方法
2. 一个后端工程师的真实故事
(1) 痛点:用户标签去重用了 500 行 if-else,效率极低
Bob 在开发用户画像系统。每个用户有多个标签(如 "VIP"、"PromoSensitive"、"HighSpender"),需要做大量集合运算:找出同时是 VIP 和高消费的用户、合并新老标签、排除黑名单标签。 他最初用数组 + 循环去重: `swift let oldTags = ["VIP", "HighSpender", "NewUser"] let newTags = ["VIP", "PromoSensitive", "HighSpender"] var merged: [String] = [] for tag in oldTags + newTags { if !merged.contains(tag) { merged.append(tag) } } 20 万用户 x O(n^2) 算法 = 服务器 CPU 跑满 20 分钟,Bob 被运维投诉了。
(2) Set 和 Dictionary 的解法
`swift let oldTags: Set = ["VIP", "HighSpender", "NewUser"] let newTags: Set = ["VIP", "PromoSensitive", "HighSpender"] let merged = oldTags.union(newTags) let common = oldTags.intersection(newTags) print("Merged: (merged)") print("Common: (common)")
(3) 收益:20 分钟 → 0.5 秒
| 维度 | 数组循环 | Set/Dictionary |
|---|---|---|
| 20 万用户去重 | 20 分钟 | 0.5 秒 |
| 代码行数 | 500+ | 30 |
| 内存占用量 | 200MB | 45MB |
| 并集运算 | 手写循环 | .union() 一行搞定 |
3. Set 集合
Set 是无序、无重复元素的集合。数组关注"顺序和重复",Set 关注"唯一性和归属判断"。 `mermaid graph TB A[Set A] --- B["{1, 2, 3}"] C[Set B] --- D["{2, 3, 4}"] E[Union] --- F["{1, 2, 3, 4}"] G[Intersection] --- H["{2, 3}"] I[Symmetric Diff] --- J["{1, 4}"] K[Subtracting] --- L["A - B = {1}"]
| 集合运算 | Swift 方法 | 结果说明 |
|---|---|---|
| 并集 | union(_:) | 两个集合的所有元素 |
| 交集 | intersection(_:) | 两个集合共有的元素 |
| 差集 | subtracting(_:) | 在 A 中但不在 B 中的元素 |
| 对称差 | symmetricDifference(_:) | 只在其中一个集合中的元素 |
| 是否是子集 | isSubset(of:) | A 的所有元素都在 B 中 |
| 是否包含 | contains(_:) | O(1) 检查元素是否存在 |
(1) 创建和基本操作
swift var fruits: Set<String`> = ["Apple", "Banana", "Orange"]
fruits.insert("Apple")
fruits.insert("Grape")
fruits.remove("Banana")
print(fruits.contains("Apple"))
print(fruits.count)
(2) 集合运算
`swift let a: Set = [1, 2, 3, 4, 5] let b: Set = [4, 5, 6, 7, 8] print("Union: (a.union(b).sorted())") print("Intersection: (a.intersection(b).sorted())") print("A - B: (a.subtracting(b).sorted())") print("Symmetric Diff: (a.symmetricDifference(b).sorted())")
▶ 示例:用户标签管理系统
swift // ============================================ // 用 Set 管理用户标签 // ============================================ var userTags: Set<String`> = ["VIP", "NewUser", "HighSpender"]
let campaignTags: Set = ["VIP", "PromoSensitive"]
let excludeTags: Set = ["Inactive", "Fraud"]
userTags.insert("iOSUser")
userTags.insert("VIP")
let targetUsers = campaignTags.subtracting(excludeTags)
print("Target tags: (targetUsers)")
let vipHighSpender = userTags.intersection(["VIP", "HighSpender"])
print("VIP high spenders: (vipHighSpender)")
let allActive = userTags.union(campaignTags).subtracting(excludeTags)
print("All active tags: (allActive.sorted())")
输出:
ext Target tags: ["VIP", "PromoSensitive"] VIP high spenders: ["VIP", "HighSpender"] All active tags: ["HighSpender", "iOSUser", "NewUser", "PromoSensitive", "VIP"]
4. Dictionary 字典
Dictionary 是无序的键值对集合,每个键唯一映射到一个值。适合"按名字查找"的场景。 `mermaid graph TB A[Dictionary] --> B["Key: Apple -> Value: 3"] A --> C["Key: Banana -> Value: 5"] A --> D["Key: Orange -> Value: 2"] B --> E[O(1) lookup by key]
| 操作 | 语法 | 说明 |
|---|---|---|
| 创建 | KeyType: ValueType | 空字典 |
| 字面量 | ["a": 1, "b": 2] | 带初始值 |
| 读取 | dict["key"] | 返回 Optional |
| 赋值 | dict["key"] = value | 新增或更新 |
| 删除 | dict["key"] = nil | 移除键值对 |
| 遍历 | or (k, v) in dict | 遍历全部 |
(1) 创建和修改
`swift var scores: [String: Int] = [:] var populations = [ "New York": 8_336_817, "Los Angeles": 3_979_576, "Chicago": 2_693_976 ] populations["Houston"] = 2_320_268 populations["New York"] = 8_400_000 populations["Chicago"] = nil if let nyPopulation = populations["New York"] { print("NY population: (nyPopulation)") }
(2) 遍历字典
`swift let capitals = [ "USA": "Washington DC", "UK": "London", "Japan": "Tokyo", "France": "Paris" ] for (country, city) in capitals { print("(country): (city)") } print("Countries: (capitals.keys.sorted())") print("Capitals: (capitals.values.sorted())")
▶ 示例:商品库存管理
`swift // ============================================ // 用 Dictionary 管理商品库存 // ============================================ var inventory: [String: Int] = [ "Laptop": 15, "Mouse": 50, "Keyboard": 30 ] inventory["Monitor"] = 10 inventory["Mouse"]! += 20 if let laptopStock = inventory["Laptop"], laptopStock > 0 { inventory["Laptop"] = laptopStock - 1 print("Shipped 1 Laptop") } let product = "Tablet" if let stock = inventory[product] { print("(product): (stock) units") } else { print("(product) not found in inventory") } print("\n=== Inventory Report ===") for (product, quantity) in inventory { let status = quantity < 20 ? "Low stock" : "In stock" print("(product): (quantity) units -- (status)") }
输出: ` ext Shipped 1 Laptop Tablet not found in inventory
=== Inventory Report === Laptop: 14 units -- Low stock Mouse: 70 units -- In stock Keyboard: 30 units -- In stock Monitor: 10 units -- Low stock `
5. Hashable 协议与嵌套集合
Dictionary 的键和 Set 的元素必须遵守 Hashable 协议——Swift 用哈希值来快速定位。
| 内置 Hashable 类型 | 需要手动实现的情况 |
|---|---|
| String, Int, Double, Bool | 自定义结构体/类 |
| Array(元素可哈希) | enum 有关联值 |
| Set, Dictionary(作为值) | 含有不可哈希属性的类型 |
(1) 自定义类型作为键
`swift struct Product: Hashable { let id: Int let name: String } var cart: [Product: Int] = [:] let laptop = Product(id: 1001, name: "Laptop") cart[laptop] = 2 print("Cart items: (cart.count)")
(2) 嵌套集合:字典的值是 Set
swift var cityTags: [String: Set<String`>] = [
"Paris": ["Eiffel Tower", "Louvre"],
"Tokyo": ["Shibuya", "Sensoji"]
]
cityTags["Paris"]?.insert("Arc de Triomphe")
cityTags["London"] = ["Big Ben", "Tower Bridge"]
for (city, landmarks) in cityTags {
print("(city): (landmarks.sorted().joined(separator: ", "))")
}
▶ 示例:用户分组统计
swift // ============================================ // 使用 Dictionary 和 Set 对用户分组统计 // ============================================ let userLanguages: [String: Set<String`>] = [
"Alice": ["Swift", "Python", "JavaScript"],
"Bob": ["Python", "Java", "Go"],
"Charlie": ["Swift", "Kotlin", "JavaScript"],
"Diana": ["Java", "C#", "Python"]
]
let swiftUsers = userLanguages.filter { .value.contains("Swift") }
print("Swift developers: (swiftUsers.count)")
let allLanguages = userLanguages.values.reduce([]) { .union() }
print("All languages: (allLanguages.sorted())")
let fullStack = userLanguages.filter { .value.count >= 3 }
for (name, langs) in fullStack {
print("Full stack: (name) -- (langs.sorted().joined(separator: ", "))")
}
输出:
ext Swift developers: 2 All languages: ["C#", "Go", "Java", "JavaScript", "Kotlin", "Python", "Swift"] Full stack: Alice -- JavaScript, Python, Swift Full stack: Charlie -- JavaScript, Kotlin, Swift Full stack: Diana -- C#, Java, Python
6. 完整示例:用户画像标签系统
swift // ============================================ // 用户画像标签系统 // 综合运用 Set 和 Dictionary 的所有知识点 // ============================================ import Foundation struct UserProfile: Hashable { let id: Int let name: String var tags: Set<String> } var users: [Int: UserProfile] = [ 1: UserProfile(id: 1, name: "Alice", tags: ["VIP", "HighSpender", "iOS"]), 2: UserProfile(id: 2, name: "Bob", tags: ["NewUser", "Android"]), 3: UserProfile(id: 3, name: "Charlie", tags: ["VIP", "Android", "HighSpender"]), 4: UserProfile(id: 4, name: "Diana", tags: ["iOS", "NewUser"]) ] let campaignTags: Set = ["VIP", "iOS"] let excludeTags: Set = ["Fraud", "Inactive"] var targetUserIds: Set<Int`> = []
for (id, profile) in users {
let effectiveTags = profile.tags.subtracting(excludeTags)
if !effectiveTags.intersection(campaignTags).isEmpty {
targetUserIds.insert(id)
}
}
print("=== Campaign Target Users ===")
for id in targetUserIds.sorted() {
if let user = users[id] {
print("(user.name) -- tags: (user.tags.sorted().joined(separator: ", "))")
}
}
print("\n=== Adding Tag: BetaTester ===")
for id in users.keys {
users[id]?.tags.insert("BetaTester")
}
print("\n=== Tag Distribution ===")
var tagCounts: [String: Int] = [:]
for (_, profile) in users {
for tag in profile.tags {
tagCounts[tag, default: 0] += 1
}
}
for (tag, count) in tagCounts.sorted(by: { .value > .value }) {
print("(tag): (count) users")
}
输出: ` ext === Campaign Target Users === Alice -- tags: HighSpender, iOS, VIP Charlie -- tags: Android, HighSpender, VIP Diana -- tags: iOS, NewUser
=== Adding Tag: BetaTester ===
=== Tag Distribution === BetaTester: 4 users Android: 2 users HighSpender: 2 users iOS: 2 users NewUser: 2 users VIP: 2 users `
❓ 常见问题
📖 小节
- Set 是无序无重复的集合,适合去重和归属判断
- Set 支持并集 union、交集 intersection、差集 subtracting、对称差 symmetricDifference
- Dictionary 是无序的键值对集合,读取返回 Optional
- Dictionary 的键和 Set 的元素必须遵守 Hashable 协议
- 添加字典元素直接赋值,删除设为 nil
- 嵌套集合(Dictionary 的值是 Set)适合复杂数据建模
📝 作业
- 基础题: 创建一个包含你喜欢的 5 本书的 Set,判断 "Swift Programming" 是否在其中,添加 2 本新书,打印最终集合。
- 进阶题: 用 Dictionary 实现一个简单英汉词典。添加 5 个单词及其翻译,实现查找功能(输入英文返回中文),处理找不到的情况。
- 挑战题: 分析一组用户数据:["Alice": ["Swift", "Python"], "Bob": ["Java", "Swift"], "Charlie": ["Python", "Go"], "Diana": ["Swift", "Go"]]。找出会 Swift 但不含 Go 的用户、会 Python 或 Java 的用户、所有语言集合(去重)、以及每门语言的人数统计。