GetHashCode()-更改项目ID,然后在字典中找到该项目-KeyNotFoundException

时间:2019-05-12 13:00:08

标签: c#

在字典中查找项目基于哈希码。哈希代码基于以下代码中的项目ID,因此我不应该更改项目ID。但是我做了一些实验,更改了项目ID并在字典中进行了搜索,但随后出现KeyNotFoundException。有人可以解释一下为什么吗?

更改point1 id后,它具有1727699806哈希码,字典中索引为0的元素具有相同的哈希码:1727699806-那么为什么会有KeyNotFoundException?

class Program
{
    public class Point
    {
        public int Id { get; set; }

        public override bool Equals(object obj)
        {
            return obj is Point point &&
                    Id == point.Id;
        }

        public override int GetHashCode()
        {
            return HashCode.Combine(Id);
        }
    }

    static void Main(string[] args)
    {
        Point point1 = new Point();
        point1.Id = 5;            

        Point point2 = new Point();
        point2.Id = 20;

        var dictionary = new Dictionary<Point, string>()
        {
            { point1, "Poland" },
            { point2, "Germany" }
        };

        point1.Id = 999; // here I change id
        var point1_hash = point1.GetHashCode(); //1727699806

        var dictionary1_hash = dictionary.ElementAt(0).Key.GetHashCode(); //1727699806
        var dictionary2_hash = dictionary.ElementAt(1).Key.GetHashCode(); //650208270

        string result = dictionary[point1]; //KeyNotFoundException
    }
}

更改point1 id后,它具有1727699806哈希码,字典中索引为0的元素具有相同的哈希码:1727699806-那么为什么会有KeyNotFoundException?

3 个答案:

答案 0 :(得分:2)

问题是对象/键放在Dictionary类内的所谓“存储桶”中。存储桶中的每个条目都具有相同的哈希码(简化后,涉及一些模运算,因此没有int.MaxValue存储桶)。当您使用键插入或检索对象时,Dictionary类仅需要查看一个存储桶,因为键具有相同的哈希码。

但是现在您已经通过更改Id属性来更改键对象的哈希码。密钥point1现在存储在它不属于的存储桶中,但是Dictionary类不知道该更改。这意味着:

  1. 使用吸气剂时,将计算出不同的哈希码,因此Dictionary类将出现在不同的存储桶中。
  2. Dictionary类将找不到密钥/条目,因为它位于先前选择的存储桶(另一个桶)中。

因此,您不得将key的对象更改为产生不同的哈希码,而将其用作Dictionary中的键。参见documentation of the Dictionary class

  

只要将对象用作Dictionary<TKey,TValue>中的键,就不得以任何影响其哈希值的方式对其进行更改。

答案 1 :(得分:1)

此答案不正确,但由于技术原因无法删除。而是请参见下面的Progman答案。

答案 2 :(得分:0)

以下是Dictionary<T>类中的相关source code

private int FindEntry(TKey key)
{
    if (buckets != null)
    {
        int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
        for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next)
        {
            if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
        }
    }
    return -1;
}

如您所见,从计算键的哈希码开始搜索键。此哈希码确定将在桶中搜索匹配条目。然后,首先通过存储的哈希码比较指定存储桶中的每个条目,然后通过键的值进行比较。因此,更改存储在字典中的实体的键会以三种不同方式破坏键查找算法。