.NET Dictionary实现如何与可变对象一起使用
我知道不建议使用“可变”对象(GetHashCode()方法在将它们用作Dictionary中的键时可以返回不同结果的对象)。
下面是我对作为哈希表实现的字典如何工作的理解:
当我添加新密钥时,例如dict.Add(m1, "initially here was m1 object");
, dict
使用GetHashCode()
方法计算m1
的哈希码。 然后它进行一些内部计算,最后将此对象放入其内部数组的某个位置。
当我使用键索引来获取值时,例如dict[m1]
, dict
再次计算哈希码。 然后它做了一些内部计算,它给了我一个对象,它位于其内部数组内部的计算位置。
但我认为有一个我无法找到的错误。
所以我们假设我有这个代码:
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(); 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); }
当我调用Mutate
方法时,键被交换。 所以我认为它会给出交换结果。 但实际上这一行: Console.WriteLine("dict[m1] = " + dict[m1]);
抛出KeyNotFoundException,我无法理解为什么。 显然我在这里遗漏了一些东西……
.NET Dictionary实现如何与可变对象一起使用
它没有。 字典的文档说明:
只要对象在
Dictionary
用作键,就不能以任何影响其哈希值的方式进行更改。
由于您在“ Dictionary
更改对象时它将无法工作。
至于为什么,这不难看出来。 我们放了一个对象。 我们假设哈希码是1
。 我们将对象放在哈希表的1
桶中。 现在,对象从Dictionary外部变异,这样它的值(和哈希码)就是2
。 现在,当有人将该对象提供给字典的索引器时,它会获得哈希码,看到它是2
,并查看2
桶。 那桶是空的,所以它说,“对不起,没有元素”。
现在让我们假设使用值和散列值1
创建一个新对象。 它被传递给Dictionary,后者看到哈希是1
。 它查看1
桶并发现该索引确实存在一个项目。 它现在使用Equals
来确定对象实际上是否相等(或者这只是一个哈希冲突)。
现在,在你的情况下,它将在这里失败,因为你没有重写Equals
,你正在使用比较引用的默认实现,因为这是一个不同的对象,它将没有相同的引用。 但是,即使您更改它以比较值,*第一个对象被突变为具有值2
而不是1
,因此它无论如何都不会匹配。 其他人建议修复这个Equals
方法,你真的应该这样做, 但它仍然无法解决你的问题 。
一旦对象发生变异,找到它的唯一方法就是如果恰好发生了变异的值是哈希冲突(这是可能的,但不太可能)。 如果不是,那么根据Equals
相等的任何东西都不会知道检查正确的桶,并且根据Equals
检查正确桶的任何东西都不会相等。
我在开始时提到的引用不仅仅是一种最佳实践。 在字典中改变项目不仅是意外的,也不是奇怪的或无法执行的。 它只是不起作用 。
现在,如果对象是可变的但是在字典中没有变异那么那很好。 这可能有点奇怪,人们可能会说这是不好的做法,即使它有效。
使字典查找具有相同的哈希码是不够的。 由于哈希冲突是可能的,因此密钥也必须等于正在查找的索引。
您的MutableObject
类不会重写Equals(object)
。 因此使用引用相等(从基类System.Object
inheritance)。
Dictionary<,>
首先(快速)找到具有正确哈希码的任何键。 然后,它会检查每个候选键,以检查其中一个是否Equals
它正在搜索的键。
因此,应该重写Equals(object)
和GetHashCode()
。 如果您只覆盖其中一个,则会收到编译器的警告 。
一旦密钥的哈希码在密钥在Dictionary<,>
变异,该密钥将(可能)错误地放在Dictionary<,>
,在错误的“桶”中,因此丢失。 它将无法找到,因为搜索它将始终发生在它未找到的存储桶中。
在此示例中,密钥丢失,因此可以再次添加:
var dict = new Dictionary(); 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(dict); // throws
在使用现有Dictionary<,>
的项初始化一个Dictionary<,>
,我实际上已经看到了类似的exception(两者都使用键类型的默认EqualityComparer<>
)。
上述就是C#学习教程:.NET Dictionary实现如何与可变对象一起使用分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/983132.html