在iOS开发中,集合类型是构建应用的基础数据结构。Swift提供了三种主要的集合类型:数组(Array)、集合(Set)和字典(Dictionary)。每种类型都有其独特的特性和适用场景。本文将深入探讨这些集合类型的内部实现、性能特点、常用操作以及在实际项目中的应用技巧。

一、数组(Array)

数组是有序的元素集合,通过索引访问元素。在Swift中,数组是值类型,这意味着当你将一个数组赋值给另一个变量时,会创建一个副本。

1.1 数组的创建与初始化

// 空数组
var emptyArray: [Int] = []
var emptyArray2 = [Int]()

// 通过字面量创建
var names = ["Alice", "Bob", "Charlie"]

// 创建指定大小的数组
var repeatedArray = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]

// 从其他集合创建
let set: Set = [1, 2, 3]
let arrayFromSet = Array(set) // 顺序不确定

1.2 数组的常用操作

// 增加元素
names.append("David") // 在末尾添加
names.insert("Eve", at: 1) // 在指定位置插入

// 删除元素
let removed = names.remove(at: 0) // 删除并返回
names.removeAll() // 清空数组

// 修改元素
names[1] = "Bob2"

// 访问元素
let first = names.first // 可选值
let last = names.last // 可选值
let element = names[2] // 直接访问

// 检查数组状态
let isEmpty = names.isEmpty
let count = names.count

1.3 数组的高级操作

// 过滤
let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.filter { $0 % 2 == 0 } // [2, 4, 6]

// 映射
let squared = numbers.map { $0 * $0 } // [1, 4, 9, 16, 25, 36]

// 归约
let sum = numbers.reduce(0, +) // 21

// 排序
let sorted = numbers.sorted() // 升序
let sortedDesc = numbers.sorted(by: >) // 降序

// 分组
let grouped = Dictionary(grouping: names) { $0.first! } // 按首字母分组

1.4 数组的性能特点

  • 随机访问:O(1)时间复杂度
  • 插入/删除:在末尾操作O(1),在中间操作O(n)
  • 内存:连续内存存储,缓存友好

1.5 实战应用:图片缓存管理器

class ImageCacheManager {
    private var cache: [String: UIImage] = [:]
    private var accessOrder: [String] = [] // LRU策略
    
    func getImage(forKey key: String) -> UIImage? {
        if let image = cache[key] {
            // 更新访问顺序
            if let index = accessOrder.firstIndex(of: key) {
                accessOrder.remove(at: index)
            }
            accessOrder.append(key)
            return image
        }
        return nil
    }
    
    func setImage(_ image: UIImage, forKey key: String) {
        cache[key] = image
        accessOrder.append(key)
        
        // 如果缓存超过限制,移除最久未使用的
        if accessOrder.count > 100 {
            if let oldestKey = accessOrder.first {
                cache.removeValue(forKey: oldestKey)
                accessOrder.removeFirst()
            }
        }
    }
}

二、集合(Set)

集合是无序的唯一元素集合。在Swift中,集合也是值类型,但要求元素必须是可哈希的(遵循Hashable协议)。

2.1 集合的创建与初始化

// 空集合
var emptySet: Set<Int> = []
var emptySet2 = Set<Int>()

// 通过字面量创建
var numbersSet: Set = [1, 2, 3, 4, 5]

// 从数组创建
let array = [1, 2, 3, 2, 1]
let setFromArray = Set(array) // [1, 2, 3]

2.2 集合的常用操作

// 添加元素
numbersSet.insert(6)

// 删除元素
numbersSet.remove(3)

// 检查包含
let contains = numbersSet.contains(2) // true

// 集合运算
let setA: Set = [1, 2, 3]
let setB: Set = [3, 4, 5]

let union = setA.union(setB) // [1, 2, 3, 4, 5]
let intersection = setA.intersection(setB) // [3]
let difference = setA.subtracting(setB) // [1, 2]
let symmetricDifference = setA.symmetricDifference(setB) // [1, 2, 4, 5]

// 检查关系
let isSubset = setA.isSubset(of: setB) // false
let isSuperset = setA.isSuperset(of: setB) // false
let isDisjoint = setA.isDisjoint(with: setB) // false

2.3 集合的性能特点

  • 插入/删除/查找:平均O(1)时间复杂度
  • 内存:哈希表实现,内存占用比数组大
  • 无序:不能通过索引访问

2.4 实战应用:去重与关系管理

// 用户标签去重
class TagManager {
    private var userTags: Set<String> = []
    
    func addTag(_ tag: String) {
        userTags.insert(tag)
    }
    
    func removeTag(_ tag: String) {
        userTags.remove(tag)
    }
    
    func getCommonTags(with other: Set<String>) -> Set<String> {
        return userTags.intersection(other)
    }
}

// 权限管理
class PermissionManager {
    private var userPermissions: Set<String> = []
    
    func grantPermission(_ permission: String) {
        userPermissions.insert(permission)
    }
    
    func hasPermission(_ permission: String) -> Bool {
        return userPermissions.contains(permission)
    }
    
    func grantAllPermissions(_ permissions: [String]) {
        userPermissions.formUnion(permissions)
    }
}

三、字典(Dictionary)

字典是键值对的无序集合,通过键来访问值。在Swift中,字典也是值类型,键必须是可哈希的。

3.1 字典的创建与初始化

// 空字典
var emptyDict: [String: Int] = [:]
var emptyDict2 = [String: Int]()

// 通过字面量创建
var scores = ["Alice": 90, "Bob": 85, "Charlie": 88]

// 从元组数组创建
let pairs = [("Alice", 90), ("Bob", 85)]
var dictFromPairs = Dictionary(uniqueKeysWithValues: pairs)

// 分组创建
let names = ["Alice", "Bob", "Alice", "Charlie"]
var grouped = Dictionary(grouping: names) { $0 } // ["Alice": ["Alice", "Alice"], "Bob": ["Bob"], "Charlie": ["Charlie"]]

3.2 字典的常用操作

// 访问值
let aliceScore = scores["Alice"] // 可选值
let bobScore = scores["Bob", default: 0] // 提供默认值

// 添加/更新
scores["David"] = 92 // 添加
scores["Alice"] = 95 // 更新

// 删除
scores.removeValue(forKey: "Bob")

// 检查状态
let isEmpty = scores.isEmpty
let count = scores.count

// 遍历
for (name, score) in scores {
    print("\(name): \(score)")
}

// 键和值的集合
let keys = Set(scores.keys) // 键的集合
let values = Array(scores.values) // 值的数组

3.3 字典的性能特点

  • 查找/插入/删除:平均O(1)时间复杂度
  • 内存:哈希表实现,内存占用较大
  • 无序:不能通过索引访问

3.4 实战应用:配置管理器

class ConfigurationManager {
    private var configurations: [String: Any] = [:]
    
    // 泛型方法获取配置
    func getConfiguration<T>(for key: String, default value: T) -> T {
        if let config = configurations[key] as? T {
            return config
        }
        return value
    }
    
    // 设置配置
    func setConfiguration<T>(_ value: T, for key: String) {
        configurations[key] = value
    }
    
    // 批量加载配置
    func loadConfigurations(from dictionary: [String: Any]) {
        configurations.merge(dictionary) { (_, new) in new }
    }
    
    // 获取所有配置
    func getAllConfigurations() -> [String: Any] {
        return configurations
    }
}

// 使用示例
let configManager = ConfigurationManager()
configManager.setConfiguration("https://api.example.com", for: "apiBaseURL")
configManager.setConfiguration(30, for: "timeoutSeconds")
configManager.setConfiguration(true, for: "enableLogging")

let baseURL = configManager.getConfiguration(for: "apiBaseURL", default: "http://localhost")
let timeout = configManager.getConfiguration(for: "timeoutSeconds", default: 10)

四、集合类型的高级特性

4.1 可哈希性(Hashable)

所有集合类型的元素(或键)必须遵循Hashable协议。对于自定义类型,需要实现Hashable。

struct User: Hashable {
    let id: Int
    let name: String
    
    // 实现Hashable需要实现hash(into:)方法
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
    }
    
    // 实现Equatable(Hashable的父协议)
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id && lhs.name == rhs.name
    }
}

// 使用自定义类型作为集合元素
var userSet: Set<User> = []
var userDict: [User: String] = [:]

4.2 集合的批量操作

// 批量添加
var numbers = [1, 2, 3]
numbers.append(contentsOf: [4, 5, 6])

// 批量删除
numbers.removeAll { $0 % 2 == 0 } // 删除所有偶数

// 批量替换
numbers.replaceSubrange(1...2, with: [10, 11, 12])

// 批量排序
numbers.sort { $0 > $1 } // 原地排序

4.3 集合的转换

// 数组转集合(去重)
let array = [1, 2, 3, 2, 1]
let set = Set(array) // [1, 2, 3]

// 集合转数组(随机顺序)
let set: Set = [1, 2, 3]
let array = Array(set) // 顺序不确定

// 字典转数组
let dict = ["Alice": 90, "Bob": 85]
let arrayFromDict = dict.map { "\($0.key): \($0.value)" } // ["Alice: 90", "Bob: 85"]

五、实战项目:联系人管理系统

下面是一个完整的实战示例,展示如何综合使用三种集合类型构建一个联系人管理系统。

// MARK: - 数据模型
struct Contact: Hashable {
    let id: UUID
    let name: String
    let phone: String
    let email: String
    let tags: Set<String> // 使用集合存储标签
    
    init(name: String, phone: String, email: String, tags: Set<String> = []) {
        self.id = UUID()
        self.name = name
        self.phone = phone
        self.email = email
        self.tags = tags
    }
}

// MARK: - 联系人管理器
class ContactManager {
    // 使用字典存储联系人,键为ID,值为联系人对象
    private var contacts: [UUID: Contact] = [:]
    
    // 使用数组维护联系人列表(按姓名排序)
    private var sortedContacts: [Contact] = []
    
    // 使用集合存储所有标签
    private var allTags: Set<String> = []
    
    // 添加联系人
    func addContact(_ contact: Contact) {
        contacts[contact.id] = contact
        sortedContacts.append(contact)
        sortedContacts.sort { $0.name < $1.name }
        allTags.formUnion(contact.tags)
    }
    
    // 删除联系人
    func removeContact(id: UUID) {
        if let contact = contacts[id] {
            contacts.removeValue(forKey: id)
            if let index = sortedContacts.firstIndex(of: contact) {
                sortedContacts.remove(at: index)
            }
            // 重新计算所有标签
            allTags = Set(contacts.values.flatMap { $0.tags })
        }
    }
    
    // 按标签搜索
    func searchByTag(_ tag: String) -> [Contact] {
        return sortedContacts.filter { $0.tags.contains(tag) }
    }
    
    // 按姓名搜索
    func searchByName(_ name: String) -> [Contact] {
        return sortedContacts.filter { $0.name.lowercased().contains(name.lowercased()) }
    }
    
    // 获取所有标签
    func getAllTags() -> [String] {
        return Array(allTags).sorted()
    }
    
    // 获取联系人数量
    func getContactCount() -> Int {
        return contacts.count
    }
    
    // 批量导入联系人
    func importContacts(_ contacts: [Contact]) {
        for contact in contacts {
            addContact(contact)
        }
    }
}

// MARK: - 使用示例
let manager = ContactManager()

// 创建联系人
let contact1 = Contact(name: "Alice", phone: "123-456-7890", email: "alice@example.com", tags: ["work", "friend"])
let contact2 = Contact(name: "Bob", phone: "234-567-8901", email: "bob@example.com", tags: ["work", "colleague"])
let contact3 = Contact(name: "Charlie", phone: "345-678-9012", email: "charlie@example.com", tags: ["family"])

// 添加联系人
manager.addContact(contact1)
manager.addContact(contact2)
manager.addContact(contact3)

// 搜索
let workContacts = manager.searchByTag("work") // Alice和Bob
let aliceContacts = manager.searchByName("Alice") // Alice

// 获取所有标签
let allTags = manager.getAllTags() // ["colleague", "family", "friend", "work"]

// 删除联系人
manager.removeContact(id: contact2.id)

// 打印结果
print("总联系人数: \(manager.getContactCount())") // 2
print("工作联系人: \(workContacts.count)") // 1(Bob已删除)

六、性能优化建议

6.1 选择合适的集合类型

操作 数组 集合 字典
随机访问 ✅ O(1)
查找元素 ❌ O(n) ✅ O(1) ✅ O(1)
插入/删除(末尾) ✅ O(1) ✅ O(1) ✅ O(1)
插入/删除(中间) ❌ O(n) ✅ O(1) ✅ O(1)
去重 ✅(键唯一)
有序

6.2 内存管理

// 避免不必要的复制
class LargeDataProcessor {
    private var largeArray: [Data] = []
    
    // 使用inout参数避免复制
    func process(data: inout [Data]) {
        // 处理数据
        data.append(Data())
    }
    
    // 使用引用类型存储大型集合
    private var sharedArray: [Data] = []
    
    func getSharedArray() -> [Data] {
        return sharedArray // 返回副本
    }
    
    func getSharedArrayReference() -> [Data] {
        return sharedArray // 返回副本(值类型)
    }
}

// 对于大型数据,考虑使用类而不是结构体
class LargeDataContainer {
    var data: [Data] = []
}

6.3 并发访问

import Foundation

class ThreadSafeCollectionManager {
    private let queue = DispatchQueue(label: "com.example.collection", attributes: .concurrent)
    private var _contacts: [UUID: Contact] = [:]
    
    // 使用读写锁
    var contacts: [UUID: Contact] {
        get {
            return queue.sync { _contacts }
        }
        set {
            queue.async(flags: .barrier) {
                self._contacts = newValue
            }
        }
    }
    
    // 使用actor(Swift 5.5+)
    actor ContactActor {
        private var contacts: [UUID: Contact] = [:]
        
        func addContact(_ contact: Contact) {
            contacts[contact.id] = contact
        }
        
        func getContact(id: UUID) -> Contact? {
            return contacts[id]
        }
    }
}

七、常见问题与解决方案

7.1 数组越界问题

// 错误示例
let array = [1, 2, 3]
// let value = array[3] // 运行时崩溃

// 正确做法
if array.indices.contains(3) {
    let value = array[3]
}

// 或者使用安全访问
let value = array[safe: 3] // 自定义扩展

// 扩展Array添加安全访问
extension Array {
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

7.2 字典键的哈希冲突

// 自定义类型的哈希实现
struct Product: Hashable {
    let id: Int
    let name: String
    let price: Double
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
        hasher.combine(price)
    }
    
    static func == (lhs: Product, rhs: Product) -> Bool {
        return lhs.id == rhs.id && lhs.name == rhs.name && lhs.price == rhs.price
    }
}

// 使用示例
var productDict: [Product: String] = [:]
let product1 = Product(id: 1, name: "iPhone", price: 999.99)
let product2 = Product(id: 1, name: "iPhone", price: 999.99)
productDict[product1] = "Apple"
productDict[product2] = "Apple" // 覆盖前一个值

7.3 集合的性能瓶颈

// 避免在循环中频繁修改集合
var numbers = [1, 2, 3, 4, 5]

// 低效做法
for number in numbers {
    if number % 2 == 0 {
        numbers.append(number * 2) // 在循环中修改数组
    }
}

// 高效做法
let evenNumbers = numbers.filter { $0 % 2 == 0 }
let doubled = evenNumbers.map { $0 * 2 }
numbers.append(contentsOf: doubled)

// 或者使用临时数组
var tempNumbers = numbers
for number in numbers {
    if number % 2 == 0 {
        tempNumbers.append(number * 2)
    }
}
numbers = tempNumbers

八、总结

在iOS开发中,正确选择和使用集合类型对应用性能和代码质量至关重要:

  1. 数组:适合需要保持顺序、频繁随机访问的场景
  2. 集合:适合需要去重、快速查找、集合运算的场景
  3. 字典:适合键值对映射、快速查找的场景

在实际项目中,经常需要组合使用这些集合类型,如联系人管理系统示例所示。理解每种类型的性能特点和适用场景,能够帮助开发者编写更高效、更健壮的代码。

记住,Swift的集合类型都是值类型,这意味着它们在赋值和传递时会创建副本。对于大型数据集,需要考虑内存使用和性能影响。在需要共享数据或处理大型数据时,可以考虑使用类或actor来管理数据。

通过合理选择集合类型、优化数据结构和算法,可以显著提升iOS应用的性能和用户体验。