使用围绕 NSUUID 的包装器 class 作为密钥的散列问题

Hashing problems using a wrapper class around NSUUID as the key

** 重写 **

好的,原来我真的问了一个不同的问题。我了解 hashValue 和 ==,所以这不相关。

我想要我的包装器 class BUUID 到 "do the right thing" 并且就像 NSUUID 在字典中的行为一样。

见下文,他们没有的地方。

import Foundation
class BUUID: NSObject {
    init?(str: String) {
        if let uuid = NSUUID(UUIDString: str) {
            _realUUID = uuid
        }
        else {
            return nil
        }
    }

    override init() {
        _realUUID = NSUUID()
    }

    private var _realUUID: NSUUID

    override var description: String { get { return _realUUID.UUIDString } }
    override var hashValue: Int { get { return _realUUID.hashValue } }
    var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
    var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }

let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")

var d = [a: "Hi"]
print("\(d[a]) \(d[b])")


let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")

var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")

结果。请注意,我可以使用 NSUUID nB 查找并取回我放在 nA 下的内容。我的 BUUID.

不是这样
a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil

nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false 
nA == nB: true
Optional("Hi") Optional("Hi")

此答案与最初提出的问题相关:为什么可以在字典中获得两个具有相同键哈希值的键值对

此示例说明 Dictionary 中的键可以具有相同的哈希值,但对于不同的键,相等操作应该 return false:

func ==(lhs: FooKey, rhs: FooKey) -> Bool {

    return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}

class FooKey: Hashable, Equatable {

    var hashValue: Int {

        get {
            return 123
        }
    }
}

var d = Dictionary<FooKey, String>()

let key1 = FooKey()
let key2 = FooKey()

d[key1] = "value1"
d[key2] = "value2"

输出

[FooKey: "value1", FooKey: "value2"]

让所有键都具有相同的散列绝对不是一件好事。在这种情况下,当搜索元素复杂度下降到 O(n)(穷举搜索)时,我们会遇到最坏的情况。但它会起作用。

所以答案是 不要 让 BUUID 从 NSObject 继承,这削弱了重写 ==.

的敏捷性

所以:

extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
    // take away all 'override' keywords, nothing to override
    // otherwise same as above
}

有趣!

从 NSObject 继承也假定 isEqual(object: AnyObject?) -> Bool 方法重载:

import Foundation
class BUUID: NSObject {
    init?(str: String) {
        if let uuid = NSUUID(UUIDString: str) {
            _realUUID = uuid
        }
        else {
            return nil
        }
    }

    override init() {
        _realUUID = NSUUID()
    }

    private var _realUUID: NSUUID

    override func isEqual(object: AnyObject?) -> Bool {
        guard let buuid = object as? BUUID else {
            return false
        }

        return buuid._realUUID == _realUUID
    }
    override var description: String { get { return _realUUID.UUIDString } }
    override var hashValue: Int { get { return _realUUID.hashValue } }
    var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
    var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }

let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")

var d = [a: "Hi"]
print("\(d[a]) \(d[b])")


let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")

var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")