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 标准库里已高度封装,优先使用而非自己造轮子。
相关推荐
报错小能手1 小时前
ios开发方向——swift并发进阶核心 Task、Actor、await 详解
开发语言·学习·ios·swift
用户79457223954131 天前
【AFNetworking】OC 时代网络请求事实标准,Alamofire 的前身
objective-c·swift
报错小能手1 天前
SwiftUI 框架 认识 SwiftUI 视图结构 + 布局
ui·ios·swift
东坡肘子1 天前
被 Vibe 摧毁的版权壁垒,与开发者的新护城河 -- 肘子的 Swift 周报 #131
人工智能·swiftui·swift
报错小能手2 天前
ios开发方向——swift错误处理:do/try/catch、Result、throws
开发语言·学习·ios·swift
小夏子_riotous2 天前
openstack的使用——5. Swift服务的基本使用
linux·运维·开发语言·分布式·云计算·openstack·swift
mCell2 天前
MacOS 下实现 AI 操控电脑(Computer Use)的思考
macos·agent·swift
用户79457223954132 天前
【DGCharts】iOS 图表渲染事实标准——8 种图表类型、高度可定制,3 行代码画出一条折线
swiftui·swift
chaoguo12343 天前
Any metadata 的内存布局
swift·metadata·value witness table
tangweiguo030519874 天前
SwiftUI布局完全指南:从入门到精通
ios·swift