在.NET中,`IEqualityComparer`中的`GetHashCode`的作用是什么?
在.NET中,`IEqualityComparer`中的`GetHashCode`的作用是什么?
我正在尝试理解接口IEqualityComparer的GetHashCode方法的作用。
以下示例来自MSDN:
using System; using System.Collections.Generic; class Example { static void Main() { try { BoxEqualityComparer boxEqC = new BoxEqualityComparer(); Dictionaryboxes = new Dictionary (boxEqC); Box redBox = new Box(4, 3, 4); Box blueBox = new Box(4, 3, 4); boxes.Add(redBox, "red"); boxes.Add(blueBox, "blue"); Console.WriteLine(redBox.GetHashCode()); Console.WriteLine(blueBox.GetHashCode()); } catch (ArgumentException argEx) { Console.WriteLine(argEx.Message); } } } public class Box { public Box(int h, int l, int w) { this.Height = h; this.Length = l; this.Width = w; } public int Height { get; set; } public int Length { get; set; } public int Width { get; set; } } class BoxEqualityComparer : IEqualityComparer { public bool Equals(Box b1, Box b2) { if (b1.Height == b2.Height & b1.Length == b2.Length & b1.Width == b2.Width) { return true; } else { return false; } } public int GetHashCode(Box bx) { int hCode = bx.Height ^ bx.Length ^ bx.Width; return hCode.GetHashCode(); } }
难道Equals方法的实现不足以比较两个Box对象吗?这是我们告诉框架用于比较对象的规则。为什么需要GetHashCode?
谢谢。
Lucian
.NET中的IEqualityComparer<T>
接口中的GetHashCode
方法的作用是什么?
Dictionary<TKey,TValue>
可以通过调用GetValue
等方法来逐个比较存储的键与要查找的键是否相等,但这样做的速度非常慢。因此,像许多基于哈希的集合一样,它依靠GetHashCode
方法来快速排除大多数不匹配的值。如果在要查找的项上调用GetHashCode
方法的结果是42,并且集合有53,917个项,但在53,914个项上调用GetHashCode
方法的结果不是42,则只需要将3个项与要查找的项进行比较。其他53,914个项可以安全地忽略。
GetHashCode
方法被包含在IEqualityComparer<T>
接口中的原因是为了允许字典的使用者将通常不互相认为相等的对象视为相等。最常见的例子是希望使用字符串作为键,但使用不区分大小写的比较。为了使其有效工作,字典将需要一种哈希函数,对于"Fox"和"FOX"将产生相同的值,但对于"box"或"zebra"将产生其他值。由于String
内置的GetHashCode
方法不是这样工作的,字典将需要从其他地方获取这样的方法,而IEqualityComparer<T>
是最合适的地方,因为这种哈希码的需求与将"Fox"和"FOX"视为相同,但与"box"或"zebra"不同的Equals
方法非常相关。
答案是:GetHashCode()方法必须与Equals()方法相辅相成。
许多关于哈希的讨论都提到了桶,但我认为将其视为排除更有用。如果比较是昂贵的,即使在没有组织成桶的集合中使用哈希也可以带来好处。
.NET中的IEqualityComparer
在.NET中,每个对象都有一个Equals方法和一个GetHashCode方法。
Equals方法用于将一个对象与另一个对象进行比较,以确定这两个对象是否相等。
GetHashCode方法生成对象的32位整数表示。由于对象可以包含的信息量没有限制,因此某些哈希码可能会被多个对象共享,因此哈希码不一定是唯一的。
字典是一种非常好的数据结构,它通过增加内存占用来换取(几乎)恒定的添加/删除/获取操作的成本。但是,它不适合用于迭代。在内部,字典包含一个存储值的桶数组。当您向字典添加键和值时,会调用键的GetHashCode方法。返回的哈希码用于确定应将键/值对存储在哪个桶的索引中。
当您想要访问值时,再次传入键。然后会调用键的GetHashCode方法,以定位包含值的桶。
当IEqualityComparer传递到字典的构造函数时,将使用IEqualityComparer.Equals和IEqualityComparer.GetHashCode方法,而不是键对象上的方法。
现在来解释为什么这两个方法都是必需的,考虑以下示例:
BoxEqualityComparer boxEqC = new BoxEqualityComparer();
Dictionary
Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);
boxes.Add(redBox, "red");
boxes.Add(blueBox, "blue");
在您的示例中使用BoxEqualityComparer.GetHashCode方法时,这两个盒子都具有相同的哈希码-100^100^25 = 1000^1000^25 = 25-即使它们显然不是同一个对象。在这种情况下,它们具有相同的哈希码的原因是因为您正在使用^(按位异或)运算符,因此100^100消除为零,1000^1000也是如此。当两个不同的对象具有相同的键时,我们称之为冲突。
当我们向字典添加两个具有相同哈希码的键/值对时,它们都存储在同一个桶中。因此,当我们想要检索一个值时,会在我们的键上调用GetHashCode方法以定位桶。由于桶中有多个值,字典会遍历桶中的所有键/值对,在键上调用Equals方法以找到正确的键。
在您发布的示例中,这两个盒子是等效的,因此Equals方法返回true。在这种情况下,字典具有两个相同的键,因此会引发异常。
所以总结一下,GetHashCode方法用于生成对象存储的地址,因此字典不需要进行搜索。它只需计算哈希码并跳转到该位置。Equals方法是一个更好的相等测试方法,但不能用于将对象映射到地址空间。