GetHashCode在.NET中的IEqualityComparer <T>中的作用是什么?

Luc*_*ian 137 .net c# iequalitycomparer gethashcode

我试图理解IEqualityComparer接口的GetHashCode方法的作用.

以下示例来自MSDN:

using System;
using System.Collections.Generic;
class Example {
    static void Main() {
        try {

            BoxEqualityComparer boxEqC = new BoxEqualityComparer();

            Dictionary<Box, String> boxes = new Dictionary<Box,
                                                string>(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<Box> {

    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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Equals方法实现不应该足以比较两个Box对象吗?这就是我们告诉框架用于比较对象的规则.为什么需要GetHashCode?

谢谢.

卢西恩

she*_*tie 198

先介绍一下背景......

.NET中的每个对象都有一个Equals方法和一个GetHashCode方法.

Equals方法用于将一个对象与另一个对象进行比较 - 以查看这两个对象是否相同.

GetHashCode方法生成对象的32位整数表示.由于对象可以包含多少信息没有限制,因此某些哈希码由多个对象共享 - 因此哈希码不一定是唯一的.

字典是一种非常酷的数据结构,可以换取更高的内存占用量,以换取(或多或少)添加/删除/获取操作的固定成本.迭代虽然是一个糟糕的选择.在内部,字典包含一个桶数组,其中可以存储值.将Key和Value添加到字典时,将在Key上调用GetHashCode方法.返回的哈希码用于确定应存储密钥/值对的桶的索引.

如果要访问"值",则再次传入密钥.在Key上调用GetHashCode方法,并找到包含Value的存储桶.

将IEqualityComparer传递到字典的构造函数时,将使用IEqualityComparer.Equals和IEqualityComparer.GetHashCode方法而不是Key对象上的方法.

现在解释为什么两种方法都是必要的,请考虑以下示例:

BoxEqualityComparer boxEqC = new BoxEqualityComparer(); 

Dictionary<Box, String> boxes = new Dictionary<Box, string>(boxEqC); 

Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);

boxes.Add(redBox, "red"); 
boxes.Add(blueBox, "blue"); 
Run Code Online (Sandbox Code Playgroud)

在您的示例中使用BoxEqualityComparer.GetHashCode方法,这两个框都具有相同的哈希码 - 100 ^ 100 ^ 25 = 1000 ^ 1000 ^ 25 = 25 - 即使它们显然不是同一个对象.在这种情况下它们是相同的哈希码的原因是因为您正在使用^(按位异或)运算符,因此100 ^ 100取消而离开零,1000 ^ 1000也是如此.当两个不同的对象具有相同的键时,我们称之为冲突.

当我们将两个具有相同哈希码的键/值对添加到字典时,它们都存储在同一个桶中.因此,当我们想要检索Value时,在我们的Key上调用GetHashCode方法来定位存储桶.由于存储桶中有多个值,因此字典会对存储桶中的所有键/值对进行迭代,调用Keys上的Equals方法以找到正确的值.

在您发布的示例中,这两个框是等效的,因此Equals方法返回true.在这种情况下,字典有两个相同的键,因此它会抛出异常.

TLDR

总而言之,GetHashCode方法用于生成存储对象的地址.所以字典不必搜索它.它只是计算哈希码并跳转到该位置.Equals方法是对等式的更好测试,但不能用于将对象映射到地址空间.

希望有所帮助

  • 对于那些想知道^ -operator是什么的,这是按位异或运算符,请参阅http://msdn.microsoft.com/en-us/library/zkacc7k1.aspx. (4认同)
  • 我喜欢你!我正在寻找一个很好的解释,你的很好,谢谢:) (3认同)
  • @Acentric:除了使用哈希码进行哈希表寻址之外,哈希码背后的*基本*思想是两个对象具有不同哈希码的知识意味着它们是不相等的并且不需要比较它们.作为推论,知道许多对象的哈希码与给定对象的哈希码不匹配意味着它们都不等于对象.使用哈希代码进行寻址基本上是一种忽略具有不同哈希码的对象的方法. (3认同)
  • 只是为了明确指出这一点:(http://msdn.microsoft.com/en-us/library/ms132155.aspx) 实现者注意事项 实现需要确保如果 Equals 方法对两个对象 x 和 y 返回 true,那么 GetHashCode 方法为 x 返回的值必须等于为 y 返回的值。 (2认同)
  • @DiegoFrehner - 你说得对.另一件可以引起人们兴趣的事情是,如果修改了对象,GetHashCode方法的值不应该变化.因此,GetHashCode所依赖的对象中的字段应该是只读的(不可变的).这里有一个解释:http://stackoverflow.com/a/4868940/469701 (2认同)
  • @Acentric:对象的哈希码不应更改,除非它以影响平等的方式发生变异。如果一个类可以以影响相等的方式发生变化,那么代码应该避免在字典中存储任何可能会暴露于在字典中时会发生变化的代码的实例。如果存储对象的代码遵守该规则,则拥有反映可变状态的哈希码可能会很有用。遗憾的是 .NET 无法更好地区分状态相等和等价,因为这两个概念都是有用的。 (2认同)

Ash*_*Ash 7

GetHashCode用于Dictionary colections,它创建用于在其中存储对象的哈希.这是一篇很好的文章,为什么以及如何使用IEqualtyComparerGetHashCode http://dotnetperls.com/iequalitycomparer

  • 更多:如果你需要比较**Equals**将是enouf,但是当你需要从Dictionary获取元素时,通过哈希更容易做到这一点,而不是使用**Equals**. (4认同)

sup*_*cat 5

虽然 aDictionary<TKey,TValue>可以让其GetValue和类似的方法调用Equals每个存储的密钥,以查看它是否与正在寻找的密钥匹配,但这会非常慢。相反,像许多基于散列的集合一样,它依赖于GetHashCode快速排除大多数不匹配的值。如果调用GetHashCode一个正在寻找的项目产生 42,并且一个集合有 53,917 个项目,但调用其中GetHashCode的 53,914 个项目产生了一个不是 42 的值,那么只有 3 个项目将需要与正在寻找的项目进行比较。其他 53,914 可以安全地忽略。

将 aGetHashCode包含在 an 中的原因是IEqualityComparer<T>允许字典的使用者可能希望将它们视为相等的对象,而这些对象通常不会将彼此视为相等的对象。最常见的例子是调用者想要使用字符串作为键但使用不区分大小写的比较。为了使其有效地工作,字典需要具有某种形式的哈希函数,该函数将为“Fox”和“FOX”产生相同的值,但希望为“box”或“zebra”产生其他值。由于GetHashCode内置的方法String不能那样工作,字典将需要从其他地方获取这样的方法,IEqualityComparer<T>Equals 认为“Fox”和“FOX”彼此相同但不是“box”或“zebra”的方法。