Swift: Swift 集合与字典教程:Set 运算和 Dictionary 用法详解

集合就像一袋彩色弹珠——每个颜色只有一颗,但混在一起不分顺序。字典就像一本电话簿——按名字快速查到号码。本课带你掌握这两种强大的数据结构。

1. 你将学到


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 `


❓ 常见问题

Q Set 和 Array 应该怎么选择?
A 需要唯一性、快速查找(O(1))、不需要顺序时用 Set。需要有序、允许重复、按索引访问时用 Array。
Q Dictionary 读取不到键时返回什么?
A 返回 nil(Optional)。所以访问字典值需要用 if-let 或 ?? 处理可选值。
Q 什么是 Hashable?为什么 Set 和 Dictionary 的键需要它?
A Hashable 是可被哈希的协议。Swift 用哈希值快速定位元素位置(O(1) 复杂度)。Int、String 等基本类型默认遵守 Hashable。
Q 自定义结构体做字典键要注意什么?
A 必须遵守 Hashable 协议。如果所有属性都已 Hashable,Swift 会自动合成 hash 方法。否则需要手动实现 hash(into:) 和 ==。
Q Dictionary 的 keys 和 values 属性返回什么?
A 返回的是集合类型——keys 是 Dictionary.Keys(可像 Set 使用), alues 是 Dictionary.Values(可像 Array 使用)。都可以直接遍历。

📖 小节


📝 作业

  1. 基础题: 创建一个包含你喜欢的 5 本书的 Set,判断 "Swift Programming" 是否在其中,添加 2 本新书,打印最终集合。
  2. 进阶题: 用 Dictionary 实现一个简单英汉词典。添加 5 个单词及其翻译,实现查找功能(输入英文返回中文),处理找不到的情况。
  3. 挑战题: 分析一组用户数据:["Alice": ["Swift", "Python"], "Bob": ["Java", "Swift"], "Charlie": ["Python", "Go"], "Diana": ["Swift", "Go"]]。找出会 Swift 但不含 Go 的用户、会 Python 或 Java 的用户、所有语言集合(去重)、以及每门语言的人数统计。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏