Swift 集合类型详解(一):Array、Set、Dictionary 全貌与选型思路

三种集合速览

类型 有序? 唯一? 键值对? 适用场景举例
Array 排行榜、聊天记录、播放队列
Set 去重标签、已读 ID 池、权限集合
Dictionary key 唯一 缓存、JSON 解析、路由参数表

共同点

  1. 泛型化:存进去和取出来都是确定的类型,不会 as? 到处飞。
  2. 值语义:赋值即拷贝,修改副本不影响原值(写时优化,放心用)。
  3. 可变性由"声明为 var / let"决定,而不是像 Objective-C 那样区分 NSMutable* 与不可变基类。

Array:有序可变列表

类型写法

swift 复制代码
// 长写法
let a1: Array<Int> = [1, 2, 3]

// 简写,推荐
let a2: [Int] = [1, 2, 3]

创建空数组的两种姿势

swift 复制代码
var numbers = [Int]()      // 写法 1:初始化器
var digits: [Int] = []     // 写法 2:空字面量,更短
// 小技巧:如果上下文已知类型,可省 "= [Int]()",直接 [] 即可

带默认值批量创建

swift 复制代码
// 生成 5 个 0.0 的 [Double]
let zeros = Array(repeating: 0.0, count: 5)

数组加法:拼接两个同类型数组

swift 复制代码
let a = Array(repeating: 1, count: 3)   // [1,1,1]
let b = Array(repeating: 2, count: 2)   // [2,2]
let c = a + b                           // [1,1,1,2,2]

数组字面量初始化(最常用)

swift 复制代码
// 类型推断
var shoppingList = ["Eggs", "Milk"]

// 显式标注
var shoppingList2: [String] = ["Eggs", "Milk"]

增删改查全套路

swift 复制代码
var shoppingList = ["Eggs", "Milk"]
print(shoppingList)
shoppingList.append("Flour")                    // 尾插
print(shoppingList)
shoppingList += ["Baking Powder"]               // 批量尾插
print(shoppingList)
shoppingList.insert("Maple Syrup", at: 0)       // 头插
print(shoppingList)
let maple = shoppingList.remove(at: 0)          // 按下标删
print(maple)
let last = shoppingList.removeLast()            // 直接弹尾,省一次 count
print(last)

// 改
shoppingList[0] = "Six eggs"
print(shoppingList)
// 批量改(可改变长度)
shoppingList[1..<3] = ["Bananas", "Apples"]     // 闭区间替换
print(shoppingList)

越界是运行时错误

Swift 的下标没有"返回 nil"版本,越界直接崩溃。

安全做法:

swift 复制代码
if index >= 0 && index < shoppingList.count {
    shoppingList[index] = "New"
}
// 或者封装一个 extension
extension Array {
    subscript(safe index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

遍历

swift 复制代码
// 只要元素
for item in shoppingList { }

// 既要索引又要元素
for (index, item) in shoppingList.enumerated() {
    print("第 \(index + 1) 项:\(item)")
}

Set:唯一值的无序口袋

唯一性 + Hashable

Set 靠哈希表实现,元素必须遵循 Hashable 协议。

Swift 的 StringIntDoubleBool 以及无关联值的 enum 都默认实现。

创建与空 Set

swift 复制代码
var letters = Set<Character>()   // 空集合
letters = []                     // 清空,但类型仍是 Set<Character>

字面量初始化

swift 复制代码
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// 类型可省,但 :Set 必须写,否则会被推断成 Array

增删查

swift 复制代码
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// 类型可省,但 :Set 必须写,否则会被推断成 Array
print(favoriteGenres.insert("Jazz"))                    // 返回 (inserted: true, memberAfterInsert: "Jazz")
if let removed = favoriteGenres.remove("Rock") {
    print(removed)
} // 安全移除
let hasFunk = favoriteGenres.contains("Funk")    // false
print(hasFunk)

集合运算:交、并、对称差、差

swift 复制代码
let odd: Set = [1, 3, 5, 7, 9]
let even: Set = [0, 2, 4, 6, 8]
let prime: Set = [2, 3, 5, 7]

print(odd.union(even))                     // 并
print(odd.intersection(even))              // 交 -> 空
print(odd.subtracting(prime))              // 差 -> [1,9]
print(odd.symmetricDifference(prime))      // 只在一边出现 -> [1,2,9]

关系判断:子集、超集、互斥

swift 复制代码
let house: Set = ["🐶", "🐱"]
let farm: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
print(house.isSubset(of: farm))        // true
print(farm.isSuperset(of: house))      // true
print(farm.isDisjoint(with: ["🐦"]))   // true,无交集

排序遍历

Set 本身无序,要顺序打印用 sorted()

swift 复制代码
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
for g in favoriteGenres.sorted() {
    print(g)
}

Dictionary:键值对的哈希表

类型写法

swift 复制代码
// 长写法
let d1: Dictionary<String, Int> = [:]

// 简写,推荐
let d2: [String: Int] = [:]

空字典与清空

swift 复制代码
var names: [Int: String] = [:]
names[16] = "sixteen"
names = [:]          // 再次变空,类型仍在

字面量初始化

swift 复制代码
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 类型推断为 [String: String]

读写与更新

swift 复制代码
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports["LHR"] = "London"                    // 新增或覆盖
airports["LHR"] = "London Heathrow"           // 覆盖

// 想拿到旧值再覆盖?用 updateValue
if let old = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("旧值是 \(old)")
}

安全读取

下标返回的是 Value?

swift 复制代码
if let name = airports["DUB"] {
    print(name)
} else {
    print("无此机场")
}

删除

swift 复制代码
airports["APL"] = nil           // 写法 1:赋 nil
if let removed = airports.removeValue(forKey: "DUB") { } // 写法 2:拿旧值

遍历 key/val/key-val

swift 复制代码
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
for (code, name) in airports {
    print("\(code): \(name)")
}

// 只要 key
for code in airports.keys {
    print(code)
}

// 只要 value
for name in airports.values {
    print(name)
}

// 转数组,方便 JSON 上报
let codeArray = [String](airports.keys)
print(codeArray)

顺序遍历

和 Set 一样,Dictionary 也不保证顺序:

swift 复制代码
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
for code in airports.keys.sorted() {
    print(code, airports[code]!)
}

实战小结:如何选型

  1. 需要"第 N 个"元素 ➜ Array
  2. 只关心"有没有",且要去重 ➜ Set
  3. 需要根据"某个 id 找实体" ➜ Dictionary
  4. 需要既有序又能去重?➜ 用 Array + Set 双持:
    • Set 负责"存在性"去重;
    • Array 负责顺序与下标。

例:

swift 复制代码
struct UniqueArray<T: Hashable> {
    private var array: [T] = []
    private var set: Set<T> = []
    
    mutating func append(_ new: T) -> Bool {
        guard !set.contains(new) else { return false }
        array.append(new)
        set.insert(new)
        return true
    }
}

容易踩的 5 个小坑

  1. let arr = [1,2,3]; arr.append(4) ➜ 编译错误:常量不可变。
  2. Set 的元素千万别用"自定义类"却忘了实现 Hashable,否则编译器直接罢工。
  3. Array 批量替换区间时,新数组长度可以不同,但别拿 NSRange 混用。
  4. Dictionary 的 updateValue 返回的是旧值,而不是新值,别写反了。
  5. 遍历字典 keys 时强制解包 ! 是安全的,但前提是"遍历期间不修改字典",否则可能崩溃。

扩展场景:集合在真实项目里的 3 个用法

  1. 聊天已读池

    服务端下发"已读消息 id 列表" → 客户端用 Set<Int64> 保存;

    本地新增一条消息时,if readIDs.contains(msg.id) 决定"是否显示已读角标"。

    优势:O(1) 查询,内存占用远低于数组。

  2. 标签去重与快速合并

    用户选择兴趣标签,多端同步。

    本地用 Set<String> 存,同步时直接 local.formUnion(remote) 完成合并,天然去重。

  3. 路由参数表

    路由 URL 解析成 [String: String],中间件按 key 读取参数;

    因 Dictionary 的哈希查找复杂度接近 O(1),万级路由也不惧。

总结

  • Array = 有序 + 可重;Set = 无序 + 唯一;Dictionary = 键值 + 哈希。
  • 可变性由 var / let 决定,不再需要 NSMutable* 那一套。
  • 所有集合都是值语义,多线程场景下"拷贝即安全",但大集合要注意写时复制的性能。
  • 遍历、区间替换、集合运算等 API 在 Swift 标准库里已高度封装,优先使用而非自己造轮子。
相关推荐
HarderCoder3 小时前
Swift 集合类型详解(二):自定义 Hashable、值语义与性能陷阱
swift
东坡肘子7 小时前
Sora 2:好模型,但未必是好生意 | 肘子的 Swift 周报 #0105
人工智能·swiftui·swift
HarderCoder18 小时前
Swift 6 并发深渊:@unchecked Sendable 与“隐式 MainActor”如何合谋杀死你的 App
swiftui·swift
HarderCoder19 小时前
告别 UIKit 生命周期:SwiftUI 视图一生全解析——从 init 到 deinit 的“隐秘角落”
swiftui·swift
HarderCoder20 小时前
Swift 中的基本运算符:从加减乘除到逻辑与或非
ios·swift
HarderCoder20 小时前
Swift 中“特性开关”实战笔记——用编译条件+EnvironmentValues优雅管理Debug/TestFlight/AppStore三环境
ios·swift
HarderCoder20 小时前
Swift 并发任务中到底该不该用 `[weak self]`?—— 从原理到实战一次讲透
ios·swift
大熊猫侯佩1 天前
天网代码反击战:Swift 三元运算符的 “一行破局” 指南
swiftui·swift·apple
大熊猫侯佩2 天前
在肖申克监狱玩转 iOS 26:安迪的 Liquid Glass 复仇计划
ios·swiftui·swift