【问题标题】:Hash code in Dictionary<TKey, TValue>Dictionary<TKey, TValue> 中的哈希码
【发布时间】:2010-09-28 05:04:28
【问题描述】:

我在玩字典,偶然发现了以下场景

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异常。

为什么会出现这种行为?

【问题讨论】:

    标签: .net dictionary hashcode


    【解决方案1】:

    在 .NET 中,GetHashCodeEquals 方法一起用于确定关于集合中存储的对象相等性。

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

    但是,请注意:几乎没有充分的理由来覆盖 GetHashCode 的内置行为。我发现这个有趣的 SO 线程讨论 GetHashCodeEquals 应该值得一读:Why is it important to override GetHashCode when Equals method is overridden?。它讨论了改变行为的优缺点、好和坏哈希函数的属性、这两种方法所需的属性以及其他好处。

    【讨论】:

    • +1 如果哈希码相等,则调用Equals 方法。哈希码实际上只是预过滤,因为比较比Equals 快得多。
    • 有道理。由于字典和哈希表为给定键返回一个且仅一个值,并且如果键已存在则抛出异常,因此哈希表中的每个槽都不会有链表。
    【解决方案2】:

    您需要覆盖Object.Equals

    Dictionary&lt;TKey, TValue&gt; 和其他基于散列的集合将散列相等视为必要不充分条件,因为存在散列冲突的可能性。在您的示例中,key-getter 找到要搜索的正确哈希桶,甚至将 obj1 视为完全相等的候选者,但由于 Equals 的默认实现基于引用相等,因此被拒绝。

    理想情况下,在你的班级上实现IEquatable&lt;T&gt;

    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;
        }
    }
    

    还请记住,只要键对象的哈希值存在于字典中,它就不能更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 2011-04-11
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多