Swift 中 Dictionary 的简单介绍,方便初学者快速入门
目录
- Dictionary 基础概念
- 创建 Dictionary
- 访问和修改 Dictionary
- Dictionary 的属性和方法
- 遍历 Dictionary
- Dictionary 的高级操作
- Dictionary 的性能考虑
- 实际应用场景
- 最佳实践
- 常见错误和陷阱
Dictionary 基础概念
什么是 Dictionary
Dictionary 是 Swift 中的一种集合类型,用于存储键值对(key-value pairs)的无序集合。每个值都与一个唯一的键关联,通过键可以快速访问对应的值。
Dictionary 的特点
- 无序性:Dictionary 中的元素没有固定的顺序
- 唯一性:键必须是唯一的,值可以重复
- 类型安全:Swift 的 Dictionary 是类型安全的
- 可变性:可以是可变的(var)或不可变的(let)
Dictionary 的语法
Dictionary<KeyType, ValueType>
// 或者简写为
[KeyType: ValueType]
创建 Dictionary
1. 空 Dictionary
// 方法1:使用类型注解
var emptyDict1: [String: Int] = [:]
// 方法2:使用 Dictionary 关键字
var emptyDict2: Dictionary<String, Int> = [:]
// 方法3:类型推断(需要提供初始值)
var emptyDict3 = [String: Int]()
// 方法4:使用 Dictionary 初始化器
var emptyDict4 = Dictionary<String, Int>()
2. 带初始值的 Dictionary
// 方法1:字面量语法
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 方法2:多行格式(推荐用于复杂数据)
var userInfo = [
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"city": "New York"
]
// 方法3:使用 Dictionary 初始化器
var colors = Dictionary(dictionaryLiteral: ("red", "#FF0000"), ("green", "#00FF00"))
3. 从其他集合创建 Dictionary
// 从数组创建 Dictionary
let names = ["Alice", "Bob", "Charlie"]
let scores = [95, 87, 92]
let scoreDict = Dictionary(uniqueKeysWithValues: zip(names, scores))
// 从元组数组创建
let pairs = [("a", 1), ("b", 2), ("c", 3)]
let dict = Dictionary(uniqueKeysWithValues: pairs)
// 使用 map 创建
let numbers = [1, 2, 3, 4, 5]
let squareDict = Dictionary(uniqueKeysWithValues: numbers.map { ($0, $0 * $0) })
4. 默认值 Dictionary
// 使用 default 参数
var wordCount = [String: Int]()
let count = wordCount["hello", default: 0] // 如果键不存在,返回默认值 0
// 使用 subscript 的 default 参数
wordCount["hello", default: 0] += 1 // 安全地增加计数
访问和修改 Dictionary
1. 访问值
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 方法1:使用下标(可能返回 nil)
let aliceScore = scores["Alice"] // Optional(95)
// 方法2:使用下标和默认值
let davidScore = scores["David", default: 0] // 0
// 方法3:使用 if let 安全解包
if let bobScore = scores["Bob"] {
print("Bob's score is \(bobScore)")
}
// 方法4:使用 guard let
guard let charlieScore = scores["Charlie"] else {
print("Charlie not found")
return
}
2. 添加和更新值
var scores = ["Alice": 95, "Bob": 87]
// 添加新键值对
scores["David"] = 89
// 更新现有值
scores["Alice"] = 98
// 使用 updateValue 方法(返回旧值)
if let oldScore = scores.updateValue(96, forKey: "Bob") {
print("Bob's old score was \(oldScore)")
}
// 条件更新
scores["Alice"] = max(scores["Alice", default: 0], 100)
3. 删除值
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 方法1:设置为 nil
scores["Bob"] = nil
// 方法2:使用 removeValue 方法(返回被删除的值)
if let removedScore = scores.removeValue(forKey: "Charlie") {
print("Removed Charlie's score: \(removedScore)")
}
// 方法3:删除所有元素
scores.removeAll()
// 方法4:删除所有元素并保持容量
scores.removeAll(keepingCapacity: true)
4. 检查键是否存在
var scores = ["Alice": 95, "Bob": 87]
// 方法1:使用 contains
if scores.keys.contains("Alice") {
print("Alice exists")
}
// 方法2:使用 index(forKey:)
if scores.index(forKey: "Bob") != nil {
print("Bob exists")
}
// 方法3:直接检查值是否为 nil
if scores["Charlie"] != nil {
print("Charlie exists")
}
Dictionary 的属性和方法
1. 基本属性
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 元素数量
print(scores.count) // 3
// 是否为空
print(scores.isEmpty) // false
// 容量
print(scores.capacity) // 当前分配的容量
// 键的集合
let allKeys = scores.keys // Dictionary<String, Int>.Keys
// 值的集合
let allValues = scores.values // Dictionary<String, Int>.Values
2. 键和值的操作
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 获取所有键
let keys = Array(scores.keys) // ["Alice", "Bob", "Charlie"]
// 获取所有值
let values = Array(scores.values) // [95, 87, 92]
// 检查键是否存在
let hasAlice = scores.keys.contains("Alice") // true
// 检查值是否存在
let hasScore95 = scores.values.contains(95) // true
// 获取最大值和最小值
let maxScore = scores.values.max() // 95
let minScore = scores.values.min() // 87
3. 合并 Dictionary
var dict1 = ["a": 1, "b": 2]
let dict2 = ["c": 3, "d": 4]
// 方法1:使用 merge 方法
dict1.merge(dict2) { current, new in
return current // 保留当前值
}
// 方法2:使用 += 操作符(Swift 5.4+)
dict1 += dict2
// 方法3:创建新的 Dictionary
let combined = dict1.merging(dict2) { current, new in
return new // 使用新值
}
4. 过滤和转换
var scores = ["Alice": 95, "Bob": 87, "Charlie": 92, "David": 78]
// 过滤
let highScores = scores.filter { $0.value >= 90 }
// ["Alice": 95, "Charlie": 92]
// 转换键
let upperCaseKeys = Dictionary(uniqueKeysWithValues: scores.map { (key, value) in
(key.uppercased(), value)
})
// 转换值
let adjustedScores = Dictionary(uniqueKeysWithValues: scores.map { (key, value) in
(key, value + 5)
})
// 同时转换键和值
let formattedScores = Dictionary(uniqueKeysWithValues: scores.map { (key, value) in
("Player: \(key)", "Score: \(value)")
})
遍历 Dictionary
1. 基本遍历
let scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 遍历键值对
for (name, score) in scores {
print("\(name): \(score)")
}
// 只遍历键
for name in scores.keys {
print("Player: \(name)")
}
// 只遍历值
for score in scores.values {
print("Score: \(score)")
}
2. 有序遍历
let scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 按键排序遍历
for (name, score) in scores.sorted(by: { $0.key < $1.key }) {
print("\(name): \(score)")
}
// 按值排序遍历
for (name, score) in scores.sorted(by: { $0.value > $1.value }) {
print("\(name): \(score)")
}
3. 使用 enumerated
let scores = ["Alice": 95, "Bob": 87, "Charlie": 92]
// 带索引的遍历
for (index, (name, score)) in scores.enumerated() {
print("\(index + 1). \(name): \(score)")
}
4. 条件遍历
let scores = ["Alice": 95, "Bob": 87, "Charlie": 92, "David": 78]
// 只遍历高分
for (name, score) in scores where score >= 90 {
print("High achiever: \(name) with score \(score)")
}
// 只遍历特定键
for (name, score) in scores where name.hasPrefix("A") {
print("A-name player: \(name) with score \(score)")
}
Dictionary 的高级操作
1. 分组操作
let students = [
("Alice", "Math"),
("Bob", "Science"),
("Charlie", "Math"),
("David", "History"),
("Eve", "Science")
]
// 按学科分组
let groupedBySubject = Dictionary(grouping: students) { $0.1 }
// ["Math": [("Alice", "Math"), ("Charlie", "Math")], ...]
// 按学科分组,只保留学生姓名
let studentsBySubject = Dictionary(grouping: students.map { $0.0 }) { student in
students.first { $0.0 == student }?.1 ?? ""
}
2. 去重和统计
let words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
// 统计单词出现次数
var wordCount = [String: Int]()
for word in words {
wordCount[word, default: 0] += 1
}
// ["apple": 3, "banana": 2, "cherry": 1]
// 使用 reduce 方法
let wordCount2 = words.reduce(into: [String: Int]()) { counts, word in
counts[word, default: 0] += 1
}
3. 嵌套 Dictionary
// 嵌套 Dictionary 结构
var userProfiles: [String: [String: Any]] = [
"alice": [
"name": "Alice Johnson",
"age": 25,
"skills": ["Swift", "Python", "JavaScript"]
],
"bob": [
"name": "Bob Smith",
"age": 30,
"skills": ["Java", "C++"]
]
]
// 访问嵌套值
if let aliceSkills = userProfiles["alice"]?["skills"] as? [String] {
print("Alice's skills: \(aliceSkills)")
}
// 添加新的嵌套数据
userProfiles["charlie"] = [
"name": "Charlie Brown",
"age": 28,
"skills": ["Swift", "Kotlin"]
]
4. 类型安全的 Dictionary
// 使用枚举作为键,确保类型安全
enum UserKey {
case name
case age
case email
}
var userData: [UserKey: Any] = [
.name: "John Doe",
.age: 30,
.email: "john@example.com"
]
// 访问值
if let name = userData[.name] as? String {
print("Name: \(name)")
}
5. 自定义 Dictionary 类型
// 创建专门的 Dictionary 类型
struct ScoreBoard {
private var scores: [String: Int] = [:]
mutating func addScore(_ score: Int, for player: String) {
scores[player] = score
}
func getScore(for player: String) -> Int? {
return scores[player]
}
func getTopPlayer() -> (player: String, score: Int)? {
return scores.max { $0.value < $1.value }
}
var allScores: [String: Int] {
return scores
}
}
var scoreBoard = ScoreBoard()
scoreBoard.addScore(95, for: "Alice")
scoreBoard.addScore(87, for: "Bob")
Dictionary 的性能考虑
1. 时间复杂度
// Dictionary 操作的时间复杂度
// 插入: O(1) 平均,O(n) 最坏情况
// 查找: O(1) 平均,O(n) 最坏情况
// 删除: O(1) 平均,O(n) 最坏情况
2. 内存优化
// 预分配容量
var largeDict = [String: Int](minimumCapacity: 1000)
// 使用 reserveCapacity 方法
var scores = [String: Int]()
scores.reserveCapacity(100)
// 清理内存
scores.removeAll(keepingCapacity: false)
3. 键的选择
// 好的键类型(实现了 Hashable)
struct Player: Hashable {
let id: Int
let name: String
}
// 避免使用复杂对象作为键
// 不好的例子:使用数组作为键
// var badDict: [[Int]: String] = [:] // 编译错误,数组不实现 Hashable
实际应用场景
1. 配置管理
// 应用配置
var appConfig: [String: Any] = [
"apiBaseUrl": "https://api.example.com",
"timeout": 30,
"retryCount": 3,
"debugMode": true
]
// 读取配置
let apiUrl = appConfig["apiBaseUrl"] as? String ?? "https://default.api.com"
let timeout = appConfig["timeout"] as? Int ?? 60
2. 缓存系统
class Cache {
private var cache: [String: Any] = [:]
private let maxSize: Int
init(maxSize: Int = 100) {
self.maxSize = maxSize
}
func set(_ value: Any, for key: String) {
if cache.count >= maxSize {
// 简单的 LRU 策略:删除第一个元素
cache.removeValue(forKey: cache.keys.first!)
}
cache[key] = value
}
func get(_ key: String) -> Any? {
return cache[key]
}
func clear() {
cache.removeAll()
}
}
3. 数据转换
// JSON 数据转换
let jsonData = [
"users": [
["id": 1, "name": "Alice", "age": 25],
["id": 2, "name": "Bob", "age": 30]
]
]
// 转换为结构化数据
struct User {
let id: Int
let name: String
let age: Int
}
var users: [Int: User] = [:]
if let userArray = jsonData["users"] as? [[String: Any]] {
for userDict in userArray {
if let id = userDict["id"] as? Int,
let name = userDict["name"] as? String,
let age = userDict["age"] as? Int {
users[id] = User(id: id, name: name, age: age)
}
}
}
4. 状态管理
// 游戏状态管理
enum GameState {
case playing
case paused
case gameOver
}
class GameManager {
private var gameState: [String: Any] = [:]
func setState(_ state: GameState, for player: String) {
gameState["\(player)_state"] = state
}
func getState(for player: String) -> GameState? {
return gameState["\(player)_state"] as? GameState
}
func setScore(_ score: Int, for player: String) {
gameState["\(player)_score"] = score
}
func getScore(for player: String) -> Int {
return gameState["\(player)_score"] as? Int ?? 0
}
}
最佳实践
1. 类型安全
// 好的做法:使用明确的类型
var userScores: [String: Int] = [:]
// 避免:使用 Any 类型
var badScores: [String: Any] = [:] // 不推荐
// 使用枚举作为键
enum ConfigKey: String {
case apiUrl = "api_url"
case timeout = "timeout"
case debugMode = "debug_mode"
}
var config: [ConfigKey: Any] = [:]
2. 错误处理
// 安全的访问方法
extension Dictionary {
func safeValue<T>(for key: Key, as type: T.Type) -> T? {
return self[key] as? T
}
func safeValue<T>(for key: Key, as type: T.Type, default defaultValue: T) -> T {
return self[key] as? T ?? defaultValue
}
}
// 使用示例
let config: [String: Any] = ["timeout": 30, "retries": 3]
let timeout: Int = config.safeValue(for: "timeout", as: Int.self, default: 60)
3. 性能优化
// 预分配容量
var largeDictionary = [String: Int](minimumCapacity: 1000)
// 批量操作
let items = ["a", "b", "c", "d", "e"]
var dict = [String: Int]()
dict.reserveCapacity(items.count)
for (index, item) in items.enumerated() {
dict[item] = index
}
4. 代码可读性
// 使用有意义的变量名
var playerScores: [String: Int] = [:]
var userPreferences: [String: Any] = [:]
// 使用类型别名提高可读性
typealias PlayerID = String
typealias Score = Int
var gameScores: [PlayerID: Score] = [:]
// 使用计算属性
struct UserProfile {
private var data: [String: Any] = [:]
var name: String {
get { return data["name"] as? String ?? "" }
set { data["name"] = newValue }
}
var age: Int {
get { return data["age"] as? Int ?? 0 }
set { data["age"] = newValue }
}
}
常见错误和陷阱
1. 强制解包
// 错误:强制解包可能为 nil 的值
let scores = ["Alice": 95, "Bob": 87]
let davidScore = scores["David"]! // 崩溃!
// 正确:使用安全解包
if let davidScore = scores["David"] {
print("David's score: \(davidScore)")
} else {
print("David not found")
}
2. 类型转换错误
// 错误:错误的类型转换
let config: [String: Any] = ["timeout": "30"]
let timeout = config["timeout"] as! Int // 崩溃!
// 正确:安全的类型转换
if let timeoutString = config["timeout"] as? String,
let timeout = Int(timeoutString) {
print("Timeout: \(timeout)")
}
3. 可变性问题
// 错误:尝试修改不可变 Dictionary
let scores = ["Alice": 95, "Bob": 87]
scores["Charlie"] = 92 // 编译错误
// 正确:使用 var 声明可变 Dictionary
var scores = ["Alice": 95, "Bob": 87]
scores["Charlie"] = 92
4. 键的唯一性
// 注意:后面的值会覆盖前面的值
var scores = ["Alice": 95, "Alice": 87]
print(scores) // ["Alice": 87]
// 使用 updateValue 来检测重复
var uniqueScores = [String: Int]()
if uniqueScores.updateValue(95, forKey: "Alice") != nil {
print("Alice's score was already set")
}
5. 内存泄漏
// 避免循环引用
class Player {
var name: String
var scores: [String: Int] = [:]
init(name: String) {
self.name = name
}
deinit {
print("Player \(name) deallocated")
}
}
// 使用 weak 引用避免循环引用
var players: [String: Player] = [:]
var player = Player(name: "Alice")
players["alice"] = player
player = nil // Player 会被正确释放
总结
Swift Dictionary 是一个强大而灵活的集合类型,提供了高效的键值对存储和访问。通过掌握其基本操作、高级功能和最佳实践,您可以构建出高效、安全和可维护的 Swift 代码。
关键要点:
- 类型安全:Dictionary 是类型安全的,编译时检查类型
- 性能:平均情况下,插入、查找、删除都是 O(1) 时间复杂度
- 灵活性:支持各种键值类型,包括自定义类型
- 安全性:使用可选类型处理可能不存在的键
- 功能性:提供丰富的内置方法和操作
推荐学习路径:
- 掌握基本语法和操作
- 理解类型安全和可选类型
- 学习高级操作和性能优化
- 实践实际应用场景
- 避免常见错误和陷阱