Nullable <t> .GetHashCode()是一个糟糕的哈希码函数吗?</t>

时间:2012-11-23 11:22:50

标签: c# .net hashcode

Nullable<T>.GetHashCode()的实施如下:

public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0;
    }
    return this.value.GetHashCode();
}

但是,如果基础值也生成哈希码0(例如bool设置为false或int32设置为0),那么我们有两个常见的不同对象状态具有相同的哈希码。在我看来,更好的实现将是类似的。

public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0xD523648A; // E.g. some arbitrary 32 bit int with a good mix of set and 
                           // unset bits (also probably a prime number).
    }
    return this.value.GetHashCode();
}

4 个答案:

答案 0 :(得分:3)

是的,你有一点意见。如果您事先知道要存储哪些数据,则始终可以编写更好的GetHashCode()实现。并不是图书馆作家所拥有的奢侈品。但是,是的,如果你有很多布尔?无论是假的还是!HasValue然后默认的实现会受到伤害。枚举和整数相同,零是一个常见值。

然而,你的论点是学术性的,改变实施成本减去一万分,你不能自己做。你能做的最好就是提交建议,正确的频道是user-voice site。掌握这一点将是困难的,祝你好运。

答案 1 :(得分:2)

让我们首先注意这个问题只是关于性能。对于正确性,哈希码不需要是唯一的或抗冲突的。但它对性能很有帮助。

实际上,这是哈希表的主要价值主张:实际上均匀分布的哈希码会导致O(1)行为。

那么哈希代码常量最有可能在实际应用程序中获得最佳性能配置文件?

当然不是0,因为0是一个常见的哈希码:0.GetHashCode() == 0。这适用于其他类型。 0最差的候选人,因为它往往会经常发生。

那么如何避免碰撞?我的建议:

static readonly int nullableDefaultHashCode = GetRandomInt32();
public override int GetHashCode()
{
    if (!this.HasValue)
        return nullableDefaultHashCode;
    else
        return this.value.GetHashCode();
}

均匀分布,不太可能发生碰撞,也没有选择任意常数的风格问题。

请注意,GetRandomInt32 可以实施为return 0xD523648A;。它仍然比return 0;更有用。但最好是查询廉价的伪随机数源。

答案 2 :(得分:1)

最后,没有值的Nullable<T>必须返回一个哈希码,该哈希码应该是一个常量。

返回一个任意常量可能看起来更安全或更合适,当在Nullable<int>的特定情况下查看时可能更加如此,但最后它只是:哈希。

Nullable<T>可以涵盖的整个集合中(无限制),零不是一个比任何其他值更好的哈希码。

答案 3 :(得分:0)

我不明白这里的担忧 - 在什么情况下表现不佳?

为什么你可以根据一个值的结果将散列函数视为差函数。

我可以看到,如果Type的多个不同的值哈希值相同,那么这将是一个问题。但是null散列到与0相同的值的事实似乎无关紧要。

据我所知,.NET哈希函数最常见的用途是Hashtable,HashSet或Dictionary键,而零和null恰好位于同一个桶中这一事实对整体性能影响不大