Set 的包含方法 returns 不同时间的不同值
Set's contains method returns different value at different time
我在考虑 Swift 如何确保 Set 的唯一性,因为我已经免费将我的一个对象从 Equatable
变成了 Hashable
,所以我想到了这个简单的 Playground
struct SimpleStruct: Hashable {
let string: String
let number: Int
static func == (lhs: SimpleStruct, rhs: SimpleStruct) -> Bool {
let areEqual = lhs.string == rhs.string
print(lhs, rhs, areEqual)
return areEqual
}
}
var set = Set<SimpleStruct>()
let first = SimpleStruct(string: "a", number: 2)
set.insert(first)
所以我的第一个问题是:
每当我在集合中插入一个新对象时,是否会调用 static func ==
方法?
我的问题来自这个想法:
对于Equatable
obj,为了做出这个决定,唯一保证两个obj相同的方法就是询问static func ==
.
的结果
对于 Hashable
obj,更快的方法是比较 hashValue
s...但是,就像我的情况一样,默认实现将同时使用 string
和 number
,与 ==
逻辑相反。
所以,为了测试 Set
的行为,我刚刚添加了一个打印语句。
我发现有时我会收到打印语句,有时却没有。就像有时候 hashValue
不足以做出这个决定......所以这个方法并不是每次都被调用。
奇怪...
所以我尝试添加两个相等的对象,想知道 set.contains
的结果是什么
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // returns true
set.contains(second)
奇迹中的奇迹,在操场上发射了几次,我得到了不同的结果,这可能会导致不可预测的结果......
添加
var hashValue: Int {
return string.hashValue
}
它消除了任何意想不到的结果,但我的疑问是:
为什么,如果没有自定义 hashValue
实现,==
有时会被调用,有时却不会?
Apple 应该避免这种意外行为吗?
Hashable
要求的综合实现使用所有存储的
struct
的属性,在您的例子中是 string
和 number
。您的实施
==
仅基于字符串:
let first = SimpleStruct(string: "a", number: 2)
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // true
print(first.hashValue == second.hashValue) // false
这违反了 Hashable
协议的要求:
Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
并导致未定义的行为。 (并且由于散列值是随机的
自 Swift 4.2 起,每个程序的行为可能会有所不同 运行。)
在您的测试中可能发生的是 second
的散列值用于确定集合的“桶”,其中的值
将被存储。这可能是也可能不是存储 first
的同一个桶。 – 但那是一个实现细节:未定义的行为就是未定义的行为,它可能导致意想不到的结果甚至
运行时间错误。
正在实施
var hashValue: Int {
return string.hashValue
}
或者(从 Swift 4.2 开始)
func hash(into hasher: inout Hasher) {
hasher.combine(string)
}
修复了违反规则的问题,从而使您的代码按预期运行。
我在考虑 Swift 如何确保 Set 的唯一性,因为我已经免费将我的一个对象从 Equatable
变成了 Hashable
,所以我想到了这个简单的 Playground
struct SimpleStruct: Hashable {
let string: String
let number: Int
static func == (lhs: SimpleStruct, rhs: SimpleStruct) -> Bool {
let areEqual = lhs.string == rhs.string
print(lhs, rhs, areEqual)
return areEqual
}
}
var set = Set<SimpleStruct>()
let first = SimpleStruct(string: "a", number: 2)
set.insert(first)
所以我的第一个问题是:
每当我在集合中插入一个新对象时,是否会调用 static func ==
方法?
我的问题来自这个想法:
对于Equatable
obj,为了做出这个决定,唯一保证两个obj相同的方法就是询问static func ==
.
对于 Hashable
obj,更快的方法是比较 hashValue
s...但是,就像我的情况一样,默认实现将同时使用 string
和 number
,与 ==
逻辑相反。
所以,为了测试 Set
的行为,我刚刚添加了一个打印语句。
我发现有时我会收到打印语句,有时却没有。就像有时候 hashValue
不足以做出这个决定......所以这个方法并不是每次都被调用。
奇怪...
所以我尝试添加两个相等的对象,想知道 set.contains
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // returns true
set.contains(second)
奇迹中的奇迹,在操场上发射了几次,我得到了不同的结果,这可能会导致不可预测的结果...... 添加
var hashValue: Int {
return string.hashValue
}
它消除了任何意想不到的结果,但我的疑问是:
为什么,如果没有自定义 hashValue
实现,==
有时会被调用,有时却不会?
Apple 应该避免这种意外行为吗?
Hashable
要求的综合实现使用所有存储的
struct
的属性,在您的例子中是 string
和 number
。您的实施
==
仅基于字符串:
let first = SimpleStruct(string: "a", number: 2)
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // true
print(first.hashValue == second.hashValue) // false
这违反了 Hashable
协议的要求:
Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
并导致未定义的行为。 (并且由于散列值是随机的 自 Swift 4.2 起,每个程序的行为可能会有所不同 运行。)
在您的测试中可能发生的是 second
的散列值用于确定集合的“桶”,其中的值
将被存储。这可能是也可能不是存储 first
的同一个桶。 – 但那是一个实现细节:未定义的行为就是未定义的行为,它可能导致意想不到的结果甚至
运行时间错误。
正在实施
var hashValue: Int {
return string.hashValue
}
或者(从 Swift 4.2 开始)
func hash(into hasher: inout Hasher) {
hasher.combine(string)
}
修复了违反规则的问题,从而使您的代码按预期运行。