6

在编写自己的ByteArray在内部使用字节数组的不可变类时,我实现了IStructuralEquatable接口。在我的实现中,我将计算哈希码的任务委托给了内部数组。在测试它时,令我大吃一惊的是,我发现我的两个不同的数组具有相同的结构哈希码,即它们从 中返回相同的值GetHashCode。重现:

IStructuralEquatable array11 = new int[] { 1, 1 };
IStructuralEquatable array12 = new int[] { 1, 2 };
IStructuralEquatable array22 = new int[] { 2, 2 };

var comparer = EqualityComparer<int>.Default;
Console.WriteLine(array11.GetHashCode(comparer));     // 32
Console.WriteLine(array12.GetHashCode(comparer));     // 32
Console.WriteLine(array22.GetHashCode(comparer));     // 64

IStructuralEquatable是相当新的和未知的,但我在某处读到它可以用来比较集合和数组的内容。我错了,还是我的 .Net 错了?

请注意,我不是在谈论Object.GetHashCode

编辑:所以,我显然错了,因为不相等的对象可能具有相等的哈希码。但是GetHashCode返回一组随机分布的值不是必需的吗?经过更多测试后,我发现具有相同第一个元素的任何两个数组都具有相同的哈希值。我仍然认为这是一种奇怪的行为。

4

3 回答 3

15

您所描述的不是错误。 GetHashCode()不保证不相等对象的唯一哈希。

来自MSDN

如果两个对象比较相等,则每个对象的 GetHashCode 方法必须返回相同的值。但是,如果两个对象比较不相等,则两个对象的 GetHashCode 方法不必返回不同的值。

编辑

GetHashCode()虽然for的 MSFT .NET 实现Array.IStructuralEquatable遵循上述 MSDN 文档中的原则,但作者似乎并未按预期实现它。

这是来自“Array.cs”的代码:

    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { 
        if (comparer == null)
            throw new ArgumentNullException("comparer"); 
        Contract.EndContractBlock();

        int ret = 0;

        for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++) {
            ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(0))); 
        } 

        return ret; 
    }

特别注意这一行:

ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(0)));

除非我弄错了,否则这0是有意的i。因此,GetHashCode()对于具有相同 max(0, n-8th) 元素的数组,总是返回相同的值,其中 n 是数组的长度。这没有(不违反文档),但显然不如0替换为i. 如果代码只是要使用数组中的单个值,也没有理由循环。

于 2012-07-29T23:59:53.607 回答
5

至少从 .NET 4.6.2 开始,此错误已得到修复。您可以通过Reference Source看到它。

ret = CombineHashCodes(ret, comparer.GetHashCode(GetValue(i)));
于 2016-09-29T20:08:18.293 回答
0

GetHashCode不为不相等的实例返回唯一值。但是,相等的实例将始终返回相同的哈希码。

引用Object.GetHashCode方法

如果两个对象比较相等,则每个对象的 GetHashCode 方法必须返回相同的值。但是,如果两个对象比较不相等,则两个对象的 GetHashCode 方法不必返回不同的值。

您的观察与文档不冲突,并且实施中没有错误。

于 2012-07-29T23:58:45.650 回答