Swift_数据类型_Dictionary

Swift 中 Dictionary 的简单介绍,方便初学者快速入门

目录

  1. Dictionary 基础概念
  2. 创建 Dictionary
  3. 访问和修改 Dictionary
  4. Dictionary 的属性和方法
  5. 遍历 Dictionary
  6. Dictionary 的高级操作
  7. Dictionary 的性能考虑
  8. 实际应用场景
  9. 最佳实践
  10. 常见错误和陷阱

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 代码。

关键要点:

  1. 类型安全:Dictionary 是类型安全的,编译时检查类型
  2. 性能:平均情况下,插入、查找、删除都是 O(1) 时间复杂度
  3. 灵活性:支持各种键值类型,包括自定义类型
  4. 安全性:使用可选类型处理可能不存在的键
  5. 功能性:提供丰富的内置方法和操作

推荐学习路径:

  1. 掌握基本语法和操作
  2. 理解类型安全和可选类型
  3. 学习高级操作和性能优化
  4. 实践实际应用场景
  5. 避免常见错误和陷阱
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容