Swift开发那些事

《Swift进阶》学习笔记之 - Hashable协议

2017-02-10  本文已影响103人  lexiaoyao20

标准库中所有的基本数据类型都是遵守 Hashable 协议的,它们包括字符串,整数,浮点数以及布尔值。不带有关联值得枚举类型也会自动遵守 Hashable

Dictionary 要求它的Key类型需要遵守 Hashable协议,如果你想要将自定义类型用作字典的键,那么你必须手动为你的类型添加 Hashable 并满足它,这需要你实现 hashValue 属性。另外,因为 Hashable 本身是对 Equatable 的扩展,因此还需要为你的类型重载 == 运算符。 你的实现必须保证哈希不变原则两个同样的实例(由你实现的 == 定义相同),必须拥有同样的哈希值。不过反过来不必为真:两个相同的哈希值的实例不一定需要相等

不同的哈希值的数量是有限制,然而很多可以被哈希的类型(比如字符串)的个数是无穷的。哈希值可能重复这一特性,意味着 Dictionary 必须能够处理哈希碰撞。

优秀哈希算法的第二个特质是它应该很快。如果你的 hashValue 实现要消耗太多时间,那么它很可能会拖慢你的程序,让你从字典的 O(1)特性中得到的好处损失殆尽。

写一个能同时做到这些要求的哈希算法并不容易。对于一些由本身就是 Hashable 的数据类型组成的类型来说,将成员的哈希值进行 “异或”(XOR) 运算往往是一个不错的起点:

//Hashable 要求
struct Person {
    var name: String
    var zipCode: Int
    var birthday: Date
}

extension Person: Equatable {
    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name
            && lhs.zipCode == rhs.zipCode
            && lhs.birthday == rhs.birthday
    }
}

extension Person: Hashable {
    var hashValue: Int {
        return name.hashValue ^ zipCode.hashValue ^ birthday.hashValue
    }
}

异或计算方法的一个限制是,这个操作本身是左右对称的(也就是说 a^b == b^a),对于某些数据的哈希计算,这有时会造成不必要的碰撞。你可以添加一个位旋转并混合使用它们来避免这个问题。

上一篇下一篇

猜你喜欢

热点阅读