如何.NET字典实现与可变对象的工作原理工作原理、字典、对象、NET

2023-09-04 08:22:57 作者:一百分情人

我明白,这是不建议使用可变对象(物体的GetHash code()方法,而他们被用作钥匙在的词典)。

下面是我的如何一个词典,其被实现为一个散列表,效果的认识:

当我加入新的关键,例如 dict.Add(M1,最初这里是M1对象); 词典计算散列$ C $ M1 。然后,它会进行一些内部的计算,最后把这个对象到其内部数组的某个位置。

在我使用的关键指标来获取值,例如词典[M1] 字典计算哈希code了。然后,它会做一些内部的计算,它给了我这是位于其内部数组内计算出的位置的对象。

不过,我觉得这是我无法找到一个错误。

所以,让我们假设我有这个code:

 类MutableObject
    {
         INT32 m_value;

         公共MutableObject(的Int32值)
         {
             m_value =值;
         }

         公共无效突变(的Int32值)
         {
             m_value =值;
         }

         公众覆盖INT GetHash code()
         {
             返回m_value;
         }
   }

   静态无效的主要(字串[] args)
   {
         MutableObject M1 =新MutableObject(1);
         MutableObject平方米=新MutableObject(2);

         VAR字典=新字典< MutableObject,字符串>();
         dict.Add(M1,最初这里是M1对象);
         dict.Add(M2,最初这里是M2对象);

         Console.WriteLine(以前突变:);
         Console.WriteLine(字典[M1] =+字典[M 1]);
         Console.WriteLine(字典[2] =+字典[2]);

         m1.Mutate(2);
         m2.Mutate(1);

         Console.WriteLine(后突变:);
         Console.WriteLine(字典[M1] =+字典[M 1]);
         Console.WriteLine(字典[2] =+字典[2]);

         Console.ReadKey(真正的);
   }
 
元组字典集合操作方法

当我称之为突变方法,密钥交换。因此,我认为它会给交换的结果。但实际上这一行: Console.WriteLine(字典[M1] =+字典[M 1]); 抛出KeyNotFoundException,我不明白为什么。很显然,我失去了一些东西在这里...

解决方案   

如何.NET字典实现与可变对象的工作原理

它没有。该文档字典状态:

  

只要一个对象被用作词典℃的密钥; TKEY的,TValue> ,它不能在任何影响其散列值的方式变化。

既然你改变的对象,而它在词典将无法正常工作。

至于为什么,它不是太难看。我们把一个对象。假设散列code是 1 。我们把对象中的我们的哈希表1 桶。现在的对象是从字典外的突变,使得它的值(和哈希code)的 2 。现在,当有人给该对象字典的索引器它得到的散列code,看到的,它的 2 ,并期待在 2 桶。这桶是空的,所以它说,对不起,没有元素。

现在让我们假设一个新的对象与 1 的价值和哈希生成。它传递给Dictionary,谁看到该散列 1 。它看起来在 1 桶,发现确实存在该索引处的项目。它现在使用等于以确定对象实际上是相等的(如果这仅仅是一个散列冲突)。

现在,你的情况,它会失败在这里,因为你不覆盖等于,你使用的比较基准的默认实现,而且因为这是一个不同对象不会具有相同的参考。但是,即使你把它改成比较值,*第一个对象突变为具有值 2 ,不是 1 ,所以无论如何都不会匹配。也有人建议解决这个等于方法,你真的应该这样做,,但它仍然不会解决您的问题的。

一旦对象被突变的发现是,如果它只是恰巧,该突变值是一个哈希冲突的唯一方法(这是可能的,但可能性不大)。如果不是的话,那么任何根据等于等于永远不会知道,检查权斗,并根据等于

我在一开始提到的报价不仅是一个最佳实践。这不只是意外或怪异或unperformant变异在字典中的项目。 这是行不通的的。

现在,如果对象是可变的,但不突变,而它在字典中的那么这很好。它可能是一个有点古怪,和的这是的人可能会说的情况是不好的做法,即使它的工作原理。

I understand that it is not advisable to use "mutable" objects (objects whose GetHashCode() method can return different results while they being used as keys in a Dictionary).

Below is my understanding of how a dictionary, which is implemented as a hash table, works:

When I am adding new key, for example dict.Add(m1, "initially here was m1 object");, dict calculates the hashcode of m1 using the GetHashCode() method. Then it does some internal calculations and finally puts this object into some position of its internal array.

When I am using the key index to get the value, for example dict[m1], dict calculates the hashcode again. Then it does some internal calculations, and it gives me an object which is located at the calculated position inside of its internal array.

But I think there is an error which I can't find.

So lets assume that I have this code:

    class MutableObject
    {
         Int32 m_value;

         public MutableObject(Int32 value)
         {
             m_value = value;
         }

         public void Mutate(Int32 value)
         {
             m_value = value;
         }

         public override int GetHashCode()
         {
             return m_value;
         }
   }

   static void Main(string[] args)
   {
         MutableObject m1 = new MutableObject(1);
         MutableObject m2 = new MutableObject(2);

         var dict = new Dictionary<MutableObject, String>();
         dict.Add(m1, "initially here was m1 object");
         dict.Add(m2, "initially here was m2 object");

         Console.WriteLine("Before mutation:");
         Console.WriteLine("dict[m1] = " + dict[m1]);
         Console.WriteLine("dict[m2] = " + dict[m2]);

         m1.Mutate(2);
         m2.Mutate(1);

         Console.WriteLine("After mutation:");
         Console.WriteLine("dict[m1] = " + dict[m1]);
         Console.WriteLine("dict[m2] = " + dict[m2]);

         Console.ReadKey(true);
   }

When I call Mutate methods, keys are swapped. So I thought it will give swapped results. But actually this line: Console.WriteLine("dict[m1] = " + dict[m1]); throws KeyNotFoundException, and I can't understand why. Obviously I am missing something here...

解决方案

How .NET Dictionary implementation works with mutable objects

It doesn't. The documentation for Dictionary states:

As long as an object is used as a key in the Dictionary<TKey, TValue>, it must not change in any way that affects its hash value.

Since you're changing the object while it's in the Dictionary it will not work.

As for why, it's not too hard to see. We put in an object. Let's assume that the hash code is 1. We put the object in the 1 bucket of our hash table. Now the object is mutated from outside the Dictionary so that it's value (and hash code) is 2. Now when someone gives that object to the dictionary's indexer it gets the hash code, see's that it's 2, and looks in the 2 bucket. That bucket is empty, so it says, "sorry, no element".

Now let's assume that a new object is created with a value and hash of 1. It's passed to the Dictionary, who sees that the hash is 1. It looks in the 1 bucket and finds that there is indeed an item at that index. It now uses Equals to determine if the objects are in fact equal (or if this is just a hash collision).

Now, in your case, it will fail here because you don't override Equals, you're using the default implementation which compares references, and since this is a different object it won't have the same reference. However, even if you changed it to compare the values, *the first object was mutated to have a value of 2, not 1, so it won't match anyway. Others have suggested fixing this Equals method, and you really should do that, but it still won't fix your problem.

Once the object is mutated the only way of finding it is if it just so happens that the mutated value is a hash collision (which is possible, but unlikely). If it's not, then anything that is equal according to Equals will never know to check the right bucket, and anything that checks the right bucket won't be equal according to Equals.

The quote I mentioned at the start isn't just a best practice. It's not just unexpected or weird or unperformant to mutate items in a dictionary. It just doesn't work.

Now, if the object is mutable but isn't mutated while it's in the dictionary then that's fine. It may be a bit odd, and that's a case people may say is bad practice, even if it works.