C# .NET字典实现如何处理可变对象
我理解不建议使用“可变”对象(对象的GetHashCode()方法可以返回不同的结果,而它们被用作 以下是我对作为哈希表实现的字典如何工作的理解: 当我添加新键时,例如C# .NET字典实现如何处理可变对象,c#,.net,data-structures,hashtable,C#,.net,Data Structures,Hashtable,我理解不建议使用“可变”对象(对象的GetHashCode()方法可以返回不同的结果,而它们被用作 以下是我对作为哈希表实现的字典如何工作的理解: 当我添加新键时,例如dict.Add(m1,“最初这里是m1对象”),dict使用GetHashCode()方法计算m1的hashcode。然后它进行一些内部计算,最后将该对象放入其内部数组的某个位置 当我使用键索引获取值时,例如dict[m1],dict再次计算hashcode。然后它做了一些内部计算,它给了我一个对象,它位于它内部数组的计算位置
dict.Add(m1,“最初这里是m1对象”)
,dict
使用GetHashCode()
方法计算m1
的hashcode。然后它进行一些内部计算,最后将该对象放入其内部数组的某个位置
当我使用键索引获取值时,例如dict[m1]
,dict
再次计算hashcode。然后它做了一些内部计算,它给了我一个对象,它位于它内部数组的计算位置
但是我认为有一个错误我找不到
假设我有以下代码:
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);
}
类可变对象
{
Int32 m_值;
公共可变对象(Int32值)
{
m_值=值;
}
public void Mutate(Int32值)
{
m_值=值;
}
公共覆盖int GetHashCode()
{
返回m_值;
}
}
静态void Main(字符串[]参数)
{
可变对象m1=新的可变对象(1);
可变对象m2=新的可变对象(2);
var dict=新字典();
dict.Add(m1,“最初这里是m1对象”);
dict.Add(m2,“最初这里是m2对象”);
控制台。WriteLine(“突变前:”);
Console.WriteLine(“dict[m1]=”+dict[m1]);
Console.WriteLine(“dict[m2]=”+dict[m2]);
m1.突变(2);
m2.突变(1);
控制台。WriteLine(“突变后:”);
Console.WriteLine(“dict[m1]=”+dict[m1]);
Console.WriteLine(“dict[m2]=”+dict[m2]);
Console.ReadKey(true);
}
当我调用
Mutate
方法时,键被交换。所以我想它会给出交换的结果。但实际上这行:Console.WriteLine(“dict[m1]=”+dict[m1])代码>抛出KeyNotFoundException,我不明白为什么。很明显,我在这里遗漏了一些东西…仅仅进行字典查找来获得相同的哈希代码是不够的。由于散列冲突是可能的,因此键也必须与正在查找的索引相等。您的MutableObject
类不会覆盖Equals(object)
。因此使用了引用相等(从基类继承而来的System.Object
)
字典
首先(快速)查找具有正确哈希代码的所有键。然后,它检查每个候选密钥,以检查其中一个是否等于它正在搜索的密钥
因此,Equals(object)
和GetHashCode()
应该一起重写。如果您只覆盖其中一个,则会从编译器收到警告
当密钥在字典
中时,一旦密钥的散列码发生变异,该密钥(可能)就会(可能)被错误地放在字典
中,被放在错误的“bucket”中,并因此丢失。它不会被找到,因为对它的搜索总是在它不在的桶中进行
在本例中,密钥丢失,因此可以再次添加:
var dict = new Dictionary<MutableObject, string>();
var m = new MutableObject(1);
dict.Add(m, "Hello");
m.Mutate(2);
dict.Add(m, "world");
foreach (var p in dict)
Console.WriteLine(p);
var otherDict = new Dictionary<MutableObject, string>(dict); // throws
var dict=newdictionary();
var m=新的可变对象(1);
dict.Add(m,“你好”);
m、 突变(2);
添加(m,“世界”);
foreach(dict中的var p)
控制台写入线(p);
var otherDict=新字典(dict);//投掷
我实际上看到过这样一个异常,在初始化一个字典
时,使用现有字典
中的项(都使用默认的EqualityComparer
作为键类型)
.NET字典实现如何处理可变对象
没有。各国:
只要对象被用作字典中的键,它就不能以任何影响其哈希值的方式进行更改
由于您正在更改对象,而该对象位于字典中,因此它将不起作用
至于原因,不难看出。我们放入一个物体。让我们假设散列代码是1
。我们将对象放入哈希表的1
桶中。现在,该对象从字典外部进行变异,使其值(和哈希代码)为2
。现在,当有人将该对象交给字典的索引器时,它会得到散列码,请看它是2
,并查看2
存储桶。那个桶是空的,所以它说,“对不起,没有元素”
现在,我们假设创建了一个新对象,其值和散列为1
。它被传递给字典,字典看到散列是1
。它在1
存储桶中查找,发现该索引中确实存在一个项。它现在使用Equals
来确定对象是否实际上相等(或者这是否只是散列冲突)
现在,在您的情况下,它将在这里失败,因为您没有覆盖Equals
,您使用的是比较引用的默认实现,并且由于这是一个不同的对象,因此它将不具有相同的引用。但是,即使您将其更改为比较值,*第一个对象的值发生了变化,变为2
,而不是1
,因此它无论如何都不会匹配。其他人建议修复这个Equals
方法,您确实应该这样做,但它仍然无法解决您的问题
一旦对象发生了变异,找到它的唯一方法就是碰巧变异的值是散列冲突(这是可能的,但不太可能)。如果不是,那么根据Equals
任何相等的东西都永远不会知道检查正确的b