open class HashableClass {
public init() {}
}
// MARK: - <Hashable>
extension HashableClass: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
// `hashValue` is deprecated starting Swift 4.2, but if you use
// earlier versions, then just override `hashValue`.
//
// public var hashValue: Int {
// return ObjectIdentifier(self).hashValue
// }
}
// MARK: - <Equatable>
extension HashableClass: Equatable {
public static func ==(lhs: HashableClass, rhs: HashableClass) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
}
class Foo {} // Note no protocols or anything else specified. Equality 'just works' for classes
let a = Foo()
let b = a
var msg = (a == b)
? "They match! :)"
: "They don't match. :("
print(msg)
// Prints They match! :)
let c = Foo()
var msg = (a == c)
? "They don't match! :)"
: "They match. :("
print(msg)
// Prints They don't match! :)
class Laa {
init(_ id:String){
self.id = id
}
let id:String
}
// Override implicit object equality and base it on ID instead of reference
extension Laa : Equatable {
static func == (lhs:Laa, rhs:Laa) -> Bool {
return lhs.id == rhs.id
}
}
extension Hee : Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(id) // Easiest to simply use ID here since that's what Equatable above is based on
}
}
最后,这里是你如何使用它...
let hee1 = Hee("A")
let hee2 = Hee("A")
let msg2 = (hee1 == hee2)
? "They match! :)"
: "They don't match. :("
print(msg2)
// Prints 'They match! :)'
let set = Set<Hee>()
set.append(hee1)
set.append(hee2)
print("Set Count: \(set.count)")
// Prints 'Set Count: 1'
class CustomClass: Equatable, Hashable {
let userId: String
let name: String
let count: Int
init(userId: String, name: String, count: Int) {
self.userId = userId
self.name = name
self.count = count
}
/* The Equatable protocol requires us to establish a predicate that will
determine if two instances of this type are equal or unequal based on
on what we consider equal and unequal. Here, userId makes the most sense
since we know they'll always be unique. And so now when we compare two
instances of this type, this is the function that will make that
comparison. */
static func == (lhs: CustomClass, rhs: CustomClass) -> Bool {
return lhs.userId == rhs.userId
}
/* The Hashable protocol is similar to Equatable in that it requires us to
establish a predicate that will determine if two instances of this type
are equal or unequal, again based on what we consider equal and
unequal, but here we must feed that property (or properties) into a
function that will produce the object's hash value. And this makes sense
because the purpose of a hash is to serve as a unique identifier. And,
again, userId makes the most sense since we know they'll always be unique.
However, if userId was not unique, then we could combine multiple
properties to (hopefully) generate a unique hash. Or we could simply
generate a UUID within each instance and use that property solely. */
func hash(into hasher: inout Hasher) {
hasher.combine(userId)
//hasher.combine(name) If userId was not unique, we could add this.
//hasher.combine(count) If that's still not enough, we could add even more.
// However, at this point, consider generating a UUID and using that.
}
}
5条答案
按热度按时间k2arahey1#
如果您使用的是类而不是结构体,则可以使用
ObjectIdentifier
结构体。请注意,您还必须为类定义==
,以便符合Equatable
(Hashable
需要它)。它看起来如下所示:7fyelxc52#
在Swift中,类型必须符合
Hashable
和Equatable
,才能在Dictionary
或Set
这样的数据结构中使用。然而,您可以通过使用对象的“对象标识符”来添加“自动一致性”。在下面的代码中,我实现了一个可重用的类来自动执行此操作。注意,Swift 4.2改变了
Hashable
的实现方式,所以不再覆盖hashValue
,而是覆盖hash(into:)
。要使用,只需使用类和子类
HashableClass
,然后一切都应该正常工作!envsm3lx3#
TL; DR:
不是用
Hashable
协议扩展类,然后复制每个类的实现,而是扩展Hashable
协议本身,将其约束为AnyObject
,然后将共享实现放在那里。这样,只需使类符合Hashable
,它们就会自动获得共享实现。细节...
正如这里的公认答案所示,一些实现将使用类类型上的扩展来实现
Hashable
。这种方法的问题是***您必须为您定义的每个类型复制实现***违反了DRY(即不要重复自己)原则。下面是这种方法的一个例子......
有很多重复的代码!
我的建议是相反的。不是扩展单个类类型,而是扩展
Hashable
协议本身,然后将其约束到AnyClass
并将实现放在那里。这样做会自动将该实现应用到***所有***类,这些类只指定符合Hashable
协议,而不需要特定于类的实现。这是这种方法的样子...
注意:虽然你可以直接将相等运算符添加到
Hashable
扩展中,但通过将其应用到Equatable
上的扩展中(Hashable
隐式地符合该扩展),你可以使用相同的技术将示例相等应用到类类型中,即使你不想或不需要哈希能力。有了以上两点,我们现在可以这样做了...
没有重复的代码。只是符合协议。
当然,如前所述,
Hashable
还隐式地提供Equatable
,所以这些现在也都可以工作了...Hashable
的类,因为类特定的定义更显式,因此它优先,它们可以和平共存。*隐含相等
更进一步说,只谈
Equatable
,如果要***隐式***(也就是说,不需要手动遵守协议)使所有对象类型都实现Equatable
,使用标识来测试相等性--我个人很想知道为什么默认情况下不是这样(如果需要,您仍然可以按类型重写它)--您可以使用带有全局定义的相等运算符的泛型,再次将约束设置为AnyObject
这是密码...
!=
操作符,与在扩展中定义==
操作符不同,编译器 * 不会 * 自动为你合成它,所以你必须显式地做这件事。*有了以上内容,您现在可以执行以下操作...
如上所述,您仍然可以使用类型特定版本的equality,因为它们比
AnyObject
版本更具体,因此可以与上面提供的默认reference-equality和平共存。下面的示例假设上述内容已经就绪,但仍然只基于
id
为Laa
定义了一个显式版本的等式。注意事项:如果要覆盖相等性的对象也实现了
Hashable
,则必须确保对应的哈希值也相等根据定义,相等的对象应生成相同的哈希值。也就是说,如果您将
Hashable
扩展限制为本文开头的AnyObject
,并简单地用Hashable
标记您的类,您将获得基于对象标识的默认实现,因此它不会匹配共享相同ID的不同类示例(因此被认为是相等的),所以你必须明确地确保也实现了散列函数。编译器不会为你捕捉到这个。同样,只有当你重写等式 * 并且 * 你的类实现了
Hashable
时,你才需要这么做。在
Hee
上实现hashable以遵循相等/hashable关系:最后,这里是你如何使用它...
hts6caw34#
Swift 5准系统
对于使类可散列的基本实现,我们必须同时遵守
Equatable
和Hashable
协议,如果我们很好地了解我们的对象,我们可以确定使用哪个或哪些属性使它可相等和可散列。j13ufse25#
另一种选择是为
AnyObject
实现对Hashable
和Equatable
协议的扩展,似乎达到了与您在Java中提到的类似的效果。它为项目中的所有类添加了一个默认行为,与只为指定的类添加这样的行为相比,它并不是更好的选择。所以,我只是为了完整性才提到它:
现在,如果你想为你的类实现特定的逻辑,你仍然可以这样做,就像这样:
为了避免向
AnyObject
添加默认行为,可以声明另一个协议,并且可以添加仅与该新协议相关的扩展: