说我有一个struct
,可能是任何东西:
struct Cube {
var x: Int
var y: Int
var z: Int
var width: Int
// ...
}
如何创建这些点的Set
,这样就不会有两个具有相同属性的对象?
let points: Set<Cube> = Set()
// Type ‘Cube’ does not conform to protocol ‘Hashable’
但是如何实现hashable并不是很明显。根据我的阅读,我需要创建一个哈希函数,但是在结构中我拥有的属性数量看起来并不容易。
答案 0 :(得分:15)
首先,Hashable
扩展Equatable
,因此您必须实施
一个==
运算符,使用所有属性比较两个值
哪个唯一标识一个立方体:
func ==(lhs: Cube, rhs: Cube) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.width == rhs.width
}
Hashable
协议仅需要
x == y
暗示x.hashValue == y.hashValue
所以
var hashValue: Int {
return 0
}
将是一个有效(和工作)的实现。但是,这会 将所有对象放在集合(或字典)的相同散列桶中, 这是无效的。更好的实现是例如
struct Cube: Hashable {
var x: Int
var y: Int
var z: Int
var width: Int
var hashValue: Int {
return x.hashValue ^ y.hashValue ^ z.hashValue ^ width.hashValue
}
}
这里&#34; XOR&#34;选择运算符^
是因为它不会溢出。
您也可以使用&#34;溢出运算符&#34; &+
。
更复杂的哈希函数能够区分 不同的值更好,以便设置操作变得更快。 另一方面,哈希函数本身的计算 会慢一些因此,我会寻找一个更好的&#34;哈希函数 只有当设定的操作成为程序中的性能瓶颈时才会出现。
更新:从 Swift 4.1(Xcode 9.4)开始,编译器可以合成
如果结构的所有成员都是==
和hashValue
方法
Equatable/Hashable
。因此,足以声明一致性:
struct Cube: Hashable {
var x: Int
var y: Int
var z: Int
var width: Int
}
答案 1 :(得分:8)
实施Hashable
协议包含两件事。首先是实现hashValue
,第二个是实现相等运算符。
让工作Hashable
协议的重要部分是相等运算符。它必须以仅返回true的方式实现,并且只有当两个结构包含相同的值时才会实现。
另一方面,只要相同的结构始终返回相同的值,您的hashValue
实现就可以返回任何内容。
hashValue
影响的唯一因素是代码的运行速度,因为当您添加或查找值时,首先运行的代码为hashValue
。如果hashValue
为两个结构返回相同的值,则它们之间的相等性将通过调用否则将被跳过的相等运算符来确定。
struct Cube: Hashable {
// satisfy Hashable requirement
var hashValue: Int {
get {
// you can return any integer here
return x &+ y &+ z &+...
// or even the same one for all structs
return 0
}
}
}
// satisfy Equatable requirement
func ==(lhs: Cube, rhs: Cube) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y .....
}