如何将具有可选属性的自定义类符合“hashable”协议

4

假设我有一个基类 "Person",我想将其添加到 Set(List)中,并因此需要符合 Hashable 和 Equatable:

class Person : Equatable, Hashable {
let firstName: String
let lastName: String
var nickname: String?
let dateOfBirth: NSDate
var hashValue: Int {
    if let nickname = nickname {
        return firstName.hashValue ^
               lastName.hashValue ^
               nickname.hashValue ^
               dateOfBirth.hashValue
    } else {
        return firstName.hashValue ^
               lastName.hashValue ^
               dateOfBirth.hashValue
    }

}

init (firstName: String, lastName: String, nickname: String, bornOn dateOfBirth: NSDate) {
    self.firstName = firstName
    self.lastName  = lastName
    self.nickname = nickname
    self.dateOfBirth = dateOfBirth
    }
}

func ==(lhs: Person, rhs: Person) -> Bool {
    return
        lhs.firstName   == rhs.firstName    &&
        lhs.lastName    == rhs.lastName     &&
        lhs.nickname    == rhs.nickname     &&
        lhs.dateOfBirth == rhs.dateOfBirth
}

这个类只有一个可选属性,这使得在创建哈希值时处理可选项相当合理。如果有两个或更多的可选属性会怎样呢?我可以很快地看出这种情况会变得难以控制。

当符合对象的哈希协议时,通常如何处理可选属性?

2个回答

7
计算哈希值不一定要基于所有属性。事实上,它不需要基于任何属性。你可以简单地返回一个硬编码的数值,但你不应该这么做。
只需返回一个或多个非可选属性的哈希值即可。
唯一的规则是两个比较相等的对象必须返回相同的哈希值。但是,并没有要求具有相同哈希值的两个对象也必须比较相等。

听起来很合理。也许这是一个单独讨论的话题,但是:减少hashValue计算的特定性是否会在未来引起问题? - BrandonLenz
1
一个糟糕的哈希值可能会导致在集合中存储对象时出现性能问题。 - rmaddy
3
==操作很耗费资源时,hashValue也可以用作其优化。如果两个对象的哈希值不同,就可以快速地将它们排除在显然不相等的范围之外。计算hashValue的时间复杂度应为O(1)。 - Rob Napier
@RobNapier 很聪明!很有道理。感谢您的额外提示。 - BrandonLenz

0

只想补充一点,一个类不需要遵循 Hashable 或 Equatable 协议就可以用作数组元素(这是我假设你所说的列表)。只有当你想将该类用作集合中的元素或字典的键时,才需要遵循 Hashable 和因此 Equatable。如 Hashable 的 API 参考 所指定的:

您可以在集合中使用符合 Hashable 协议的任何类型,也可以将其用作字典键。


3
如果你想使用诸如index(of:)contains方法在数组中查找对象,那么你需要符合Equatable协议。 - rmaddy
是的,为了方便起见,如果您想避免将闭包传递给 index(of:)contains,只需传递元素本身即可。 - J.beenie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接