字典中的哈希码<tkey,tvalue =“”> </tkey,>

时间:2010-09-28 05:04:29

标签: .net dictionary hashcode

我正在玩“词典”并偶然发现了下面的情景

public class MyObject
{
    public string I { get; set; }
    public string J { get; set; }
    public string K { get; set; }

    public override int GetHashCode()
    {
        int hashCode = (I+J+K).GetHashCode();
        Debugger.Log(9, "INFO", hashCode.ToString() + System.Environment.NewLine);
        return hashCode;
    }
}
class Program
{
    static void Main(string[] args)
    {
        MyObject obj1 = new MyObject() { I = "Hello", J = "World" };
        MyObject obj2 = new MyObject() { I = "Hello", J = "World" };

        Dictionary<MyObject, string> collection = new Dictionary<MyObject, string>();
        collection.Add(obj1, "1");
        var result = collection[obj2]; // KeyNotFound exception here.
    }
}

我有MyObject类作为字典的键,我重写GetHashCode方法,根据存储在类中的值返回哈希码。

因此,当执行上面的代码时,obj1和obj2都返回相同的哈希码,但是字典仍会抛出KeyNotFound异常。

出现这种行为的原因是什么?

2 个答案:

答案 0 :(得分:7)

在.NET中,GetHashCodeEquals方法一起使用,以确定与集合中的存储有关的对象。

请注意,哈希表比通过哈希码简单地将密钥映射到单个槽更复杂。由于哈希码的性质,可能会发生冲突并且在实践中发生(尽管具有良好的哈希函数,这不应该经常发生)。因此,大多数散列表实现必须处理生成相同散列码的两个不同对象的情况,并且这通常通过散列表中的每个“时隙”处的链表来实现。哈希码用于确定槽,Equals方法用于确定对象存储在链表中的位置(在哈希表的大多数“标准”实现中)。

然而,一句警告:很少有充分的理由来覆盖GetHashCode的内置行为。我发现这个有趣的SO帖子讨论GetHashCodeEquals,值得一读:Why is it important to override GetHashCode when Equals method is overridden?。它讨论了改变行为的优点/缺点,好的和坏的散列函数的属性,这两种方法所需的属性以及其他好处。

答案 1 :(得分:3)

您需要覆盖Object.Equals

Dictionary<TKey, TValue>和其他基于散列的集合将散列等式视为必需 条件不足以完全相等,因为散列冲突的可能性。在您的示例中,key-getter找到要搜索的正确哈希桶,甚至将obj1视为完全相等的候选者,但因为Equals的默认实现基于引用相等,它被拒绝了。

理想情况下,在您的课程上实施IEquatable<T>

public class MyObject : IEquatable<MyObject>
{
    public string I { get; set; }
    public string J { get; set; }
    public string K { get; set; }

    public override int GetHashCode()
    {
        // you might want to consider a better hash-function here.
        return (I + J + K).GetHashCode();
    }

    public override bool Equals(object obj)
    {
        return base.Equals(obj as MyObject);
    }

    public bool Equals(MyObject other)
    {
        return other != null && other.I == I && other.J == J && other.K == K;
    }
}

还要记住,只要键字对象存在于字典中,它就不能改变。