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

使用Swift Set存储Hashable对象的异常问题及解决办法

用Set跟踪带唯一键的可变对象的正确姿势

要通过Set跟踪拥有唯一键但数据可变的对象时,Set要求元素必须遵循Hashable协议,但如果只实现基于键的哈希方法,会出现同键对象重复存入的问题。

错误示例:同键对象重复存入

只实现Hashable的哈希方法,仅用key计算哈希值,但未自定义相等判断逻辑时,update(with:)无法正确替换同键条目:

struct Value: Hashable
{
    let key: Int
    var contents: Int
    func hash(into hasher: inout Hasher) {
        hasher.combine(key)
    }
}

var set = Set<Value>()

for key in 1...5 {
    set.insert(Value(key: key, contents: key))
}
set.update(with: Value(key: 1, contents: 1000))
print("Set contains key 1, value 1. == \(set.contains(Value(key:1, contents: 1)))")
print("Set contains key 1, value 1000. == \(set.contains(Value(key:1, contents: 1000)))")

set.forEach { print($0, $0.hashValue) }

运行后会发现Set中存在两个key=1的对象,update(with:)并未替换原有条目,反而新增了重复项。

问题根源与解决方法

Set的底层逻辑是:先通过哈希值定位元素位置,再用==判断元素是否相等。如果只实现哈希方法,Swift会自动生成基于所有属性的==实现,导致同键但contents不同的对象被判定为不相等,因此无法触发替换逻辑。

正确的做法是同时遵循Hashable和Equatable协议,自定义==方法,仅基于key判断对象相等:

struct Value: Hashable, Equatable
{
    let key: Int
    var contents: Int
    func hash(into hasher: inout Hasher) {
        hasher.combine(key)
    }
    static func == (lhs: Value, rhs: Value) -> Bool {
        lhs.key == rhs.key
    }
}

var set = Set<Value>()

for key in 1...5 {
    set.insert(Value(key: key, contents: key))
}
set.update(with: Value(key: 1, contents: 1000))
print("Set contains key 1, value 12345 == \(set.contains(Value(key:1, contents: 12345))) (contents ignored)") // contents ignored

set.forEach { print($0, $0.hashValue) }

此时update(with:)会正确替换同键条目,Set中不会出现重复的key对象,contains判断也会忽略contents的差异,仅以key为准。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 20:52:20