TLDR
我的自定义结构实现了Hashable Protocol。但是,当在 a 中插入键时发生哈希冲突时Dictionary
,不会自动处理它们。我该如何克服这个问题?
背景
我之前曾问过这个问题 How to implement the Hashable Protocol in Swift for an Int array (a custom string struct)。后来我添加了自己的答案,这似乎有效。
但是,最近我hashValue
在使用Dictionary
.
最基本的例子
我已尽可能将代码简化为以下示例。
自定义结构
struct MyStructure: Hashable {
var id: Int
init(id: Int) {
self.id = id
}
var hashValue: Int {
get {
// contrived to produce a hashValue collision for id=1 and id=2
if id == 1 {
return 2
}
return id
}
}
}
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}
请注意重载相等运算符 (==) 的全局函数,以符合Hashable 协议所要求的 Equatable Protocol 。
微妙的字典关键问题
如果我创建一个Dictionary
withMyStructure
作为键
var dictionary = [MyStructure : String]()
let ok = MyStructure(id: 0) // hashValue = 0
let collision1 = MyStructure(id: 1) // hashValue = 2
let collision2 = MyStructure(id: 2) // hashValue = 2
dictionary[ok] = "some text"
dictionary[collision1] = "other text"
dictionary[collision2] = "more text"
print(dictionary) // [MyStructure(id: 2): more text, MyStructure(id: 0): some text]
print(dictionary.count) // 2
相等的哈希值会导致collision1
键被键覆盖collision2
。没有警告。如果这样的冲突在有 100 个键的字典中只发生一次,那么它很容易被遗漏。(我花了很长时间才注意到这个问题。)
字典文字的明显问题
但是,如果我用字典文字重复这一点,问题就会变得更加明显,因为抛出了一个致命错误。
let ok = MyStructure(id: 0) // hashValue = 0
let collision1 = MyStructure(id: 1) // hashValue = 2
let collision2 = MyStructure(id: 2) // hashValue = 2
let dictionaryLiteral = [
ok : "some text",
collision1 : "other text",
collision2 : "more text"
]
// fatal error: Dictionary literal contains duplicate keys
问题
我的印象是没有必要hashValue
总是返回一个唯一的值。例如,马特汤普森说,
关于实现自定义散列函数的最常见误解之一来自......认为散列值必须是不同的。
受人尊敬的 SO 用户@Gaffa 说,处理哈希冲突的一种方法是
考虑哈希码是非唯一的,并对实际数据使用相等比较器来确定唯一性。
在我看来,快速哈希协议哈希函数是否需要返回唯一值?在撰写本文时尚未得到充分回答。
阅读 SwiftDictionary
问题后如何处理哈希冲突?,我假设 Swift 会自动处理与Dictionary
. 但如果我使用自定义类或结构,显然情况并非如此。
这个评论让我觉得答案在于 Equatable 协议是如何实现的,但我不确定我应该如何改变它。
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}
是为每个字典键查找调用此函数还是仅在存在哈希冲突时调用此函数?(更新:看到这个问题)
当(且仅当)发生哈希冲突时,我应该怎么做才能确定唯一性?