You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建类似Set的Identifiable对象集合?解决Hashable重复问题

解决Identifiable对象集合的重复与更新问题

这个问题确实戳中了Swift里用可变Identifiable对象管理集合的痛点——Set依赖Hashable协议,但可变属性的变更会导致哈希值改变,进而出现重复项,而我们真正想要的是用id作为唯一标识来管理集合,对吧?下面给你几个实用的解决方案:

方案1:用字典以ID为Key存储

最直接的办法就是用字典,把Identifiable的id作为字典的Key,对象本身作为Value。这样不管对象的其他属性怎么变,只要id不变,更新时就会直接覆盖旧值,完全不会有重复问题:

struct Parcel: Identifiable { 
    let id: String 
    var location: Int? 
}

// 初始化字典
var item = Parcel(id: "123")
var parcelDict: [String: Parcel] = [item.id: item]

// 修改属性后更新字典
item.location = 33435
parcelDict[item.id] = item

字典的查找、插入、更新操作都是O(1)复杂度,和Set的效率差不多,而且逻辑非常直观。

方案2:自定义基于Identifiable的集合类型

如果你更习惯Set的使用方式,可以自己封装一个专门针对Identifiable对象的集合,内部用字典做存储,对外暴露类似Set的接口:

struct IdentifiableSet<Element: Identifiable> {
    private var storage: [Element.ID: Element] = [:]
    
    // 插入元素(自动覆盖同ID的旧元素)
    mutating func insert(_ element: Element) {
        storage[element.id] = element
    }
    
    // 更新元素(和insert逻辑一致,语义更清晰)
    mutating func update(with element: Element) {
        storage[element.id] = element
    }
    
    // 判断是否包含某个元素(基于ID)
    func contains(_ element: Element) -> Bool {
        storage.keys.contains(element.id)
    }
    
    // 按需扩展其他方法,比如获取所有元素、移除元素等
    var elements: [Element] {
        Array(storage.values)
    }
}

// 使用示例
var item = Parcel(id: "123")
var parcelSet = IdentifiableSet<Parcel>()
parcelSet.insert(item)

item.location = 33435
parcelSet.update(with: item)
// 此时集合里只有一个元素,不会出现重复

这个方案既保留了集合的使用习惯,又完全基于id来管理元素,完美避开了Hashable带来的可变属性问题。

方案3:修改Hashable实现,仅基于ID计算哈希(谨慎使用)

如果一定要用原生Set,可以重写Hashable的实现,让哈希值和相等判断只依赖id,忽略其他可变属性:

struct Parcel: Identifiable, Hashable { 
    let id: String 
    var location: Int? 
    
    // 只将id加入哈希计算
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    // 仅比较id判断是否相等
    static func == (lhs: Parcel, rhs: Parcel) -> Bool {
        lhs.id == rhs.id
    }
}

这样修改后,就算location变更,item的哈希值也不会变,调用list.update(with: item)时就能正确找到原对象并更新,不会添加重复项。不过要注意:这样的Hashable实现意味着“只要id相同就是同一个对象”,你需要确保这符合你的业务逻辑——如果业务上允许两个id相同但属性不同的对象存在,这个方案就不适用。


内容的提问来源于stack exchange,提问作者TruMan1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 17:57:32