Swift-十进制';s哈希值与X==-X相同,不能用于比较哈希值

Swift-十进制';s哈希值与X==-X相同,不能用于比较哈希值,swift,decimal,hashable,Swift,Decimal,Hashable,我们发现,如果一个十进制数是另一个十进制数的负数,则无法通过哈希值区分两个十进制数。我们使用小数作为结构中的一个字段,该结构实现了Hashable,可以放入一个集合中。然后,我们的业务逻辑要求所有字段都是唯一的,因此所有字段和组合都是hashValue。这意味着两个结构,其中十进制字段是另一个字段的负数,其余字段实际上相等,则整个结构被视为相等。这不是我们想要的 游乐场守则: 对于0中的i..相同的对象必须具有相同的哈希值,但不是相反:不同的对象可以具有相同的哈希值。相等性测试必须使用==完成,

我们发现,如果一个十进制数是另一个十进制数的负数,则无法通过哈希值区分两个十进制数。我们使用小数作为结构中的一个字段,该结构实现了Hashable,可以放入一个集合中。然后,我们的业务逻辑要求所有字段都是唯一的,因此所有字段和组合都是hashValue。这意味着两个结构,其中十进制字段是另一个字段的负数,其余字段实际上相等,则整个结构被视为相等。这不是我们想要的

游乐场守则:


对于0中的i..相同的对象必须具有相同的哈希值,但不是相反:不同的对象可以具有相同的哈希值。相等性测试必须使用
==
完成,并且决不能仅依赖哈希值

在这种特殊情况下,请注意有超过264个
十进制值,因此实际上不可能为所有值分配不同的哈希值。(与字符串、数组、字典等类似)

如果您有一个包含
Decimal
(可能还有其他)属性的自定义结构,那么
equalable
Hashable
协议的实现应该如下所示:

struct Foo: Hashable {

    let value: Decimal
    let otherValue: Int

    static func == (lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value && lhs.otherValue == rhs.otherValue
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(value)
        hasher.combine(otherValue)
    }
}
请注意,如果所有存储的属性都是可散列的,那么编译器可以自动合成这些方法,并且声明一致性就足够了:

struct Foo: Hashable {
    let value: Decimal
    let otherValue: Int
}

< P> >:我假设行为是从基础类型<代码> NSDECIMALNATION>代码继承的。对于Xcode 11 beta(Swift 5.1)
x
-x
具有不同的哈希值,如
decimalnumber
,但哈希值与
NSDecimalNumber
相同:

let d1: Decimal = 123
let d2: Decimal = -123

print(d1.hashValue) // 1891002061093723710
print(d2.hashValue) // -6669334682005615919

print(NSDecimalNumber(decimal: d1).hashValue) // 326495598603
print(NSDecimalNumber(decimal: d2).hashValue) // 326495598603

(由于散列值从Swift 4.2开始是随机化的,因此您的值可能会有所不同。)但上述规定仍然适用:始终可能存在冲突,并且不能依赖具有不同散列的不同值。

根据定义,散列值的公平性是必要的,但不足以使项目相等。那么问题是什么呢?@MarekR问题是,正如我在帖子中所描述的,我们的结构中有一个小数点,还有其他字段。当人们期望它们有所不同时,它们就不是了。正是在编写单元测试时,我发现其中一个失败了,因为当我们称之为
delta
的字段在两个结构之间存在这种关系时,我无法将两个结构插入到同一集中structs@Simon:这意味着
==
在“其他结构”中的实现错误。哈希值用于性能优化。这是一个快速而肮脏的平等检查。如果哈希值不同==>则不相等。如果散列是相同的,dang,那么您现在必须多走一步,在==中进行详尽的比较。代码不应将哈希值用于除性能之外的任何内容。如果所有值都具有完全相同的哈希值,则代码应能正确运行(如果运行缓慢)。为确保准确性,请编写等式(
==
)函数。为性能提供良好的哈希函数。如果散列匹配,一个集合将只调用
==
,但是如果您正确编写了
=
函数,那么一个带X的结构和另一个带-X的结构将无法通过
=
测试,因此即使它们返回相同的散列值,它们也将适合于同一集合。不可能所有数字都有唯一的散列值,但是OP描述的问题不是散列冲突,而是一个错误的散列算法。随机散列冲突是一回事,但是一个数字总是和它的负数有相同的散列是另一回事。这就是说,依赖哈希是一个很差的设计决定。注意浮点有<代码> -0 和<代码> + 0 < /代码>,并且这些值仍然是相等的。@ MaGuZBC:一些基础类型有非常糟糕的哈希值。例如,
NSArray
返回元素数:)@MartinR请参阅我的编辑,以便对Integer执行相同的操作。我认为你的第二段没有回答我的问题。它系统地为值X及其否定生成相同的哈希,这与不可能为每个十进制值分配唯一的哈希不同。@Simon:所有存储的属性都会进行相等性比较。您可以在中找到详细信息。