简体   繁体   English

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

[英]What's the role of GetHashCode in the IEqualityComparer<T> in .NET?

I'm trying to understand the role of the GetHashCode method of the interface IEqualityComparer.我试图了解接口 IEqualityComparer 的 GetHashCode 方法的作用。

The following example is taken from MSDN:以下示例取自 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();
    }
}

Shouldn't the Equals method implementation be enough to compare two Box objects?难道 Equals 方法实现不足以比较两个 Box 对象吗? That is where we tell the framework the rule used to compare the objects.这就是我们告诉框架用于比较对象的规则的地方。 Why is the GetHashCode needed?为什么需要 GetHashCode?

Thanks.谢谢。

Lucian卢西安

A bit of background first...首先介绍一下背景...

Every object in .NET has an Equals method and a GetHashCode method. .NET 中的每个对象都有一个 Equals 方法和一个 GetHashCode 方法。

The Equals method is used to compare one object with another object - to see if the two objects are equivalent. Equals 方法用于将一个对象与另一个对象进行比较 - 以查看两个对象是否等效。

The GetHashCode method generates a 32-bit integer representation of the object. GetHashCode 方法生成对象的 32 位整数表示形式。 Since there is no limit to how much information an object can contain, certain hash codes are shared by multiple objects - so the hash code is not necessarily unique.由于对一个对象可以包含多少信息没有限制,某些散列码由多个对象共享——因此散列码不一定是唯一的。

A dictionary is a really cool data structure that trades a higher memory footprint in return for (more or less) constant costs for Add/Remove/Get operations.字典是一种非常酷的数据结构,它以更高的内存占用为代价换取(或多或少)添加/删除/获取操作的恒定成本。 It is a poor choice for iterating over though.但是,这是迭代的糟糕选择。 Internally, a dictionary contains an array of buckets, where values can be stored.在内部,字典包含一个桶数组,其中可以存储值。 When you add a Key and Value to a dictionary, the GetHashCode method is called on the Key.将 Key 和 Value 添加到字典时,会在 Key 上调用 GetHashCode 方法。 The hashcode returned is used to determine the index of the bucket in which the Key/Value pair should be stored.返回的哈希码用于确定应存储键/值对的桶的索引。

When you want to access the Value, you pass in the Key again.当您要访问 Value 时,您再次传入 Key。 The GetHashCode method is called on the Key, and the bucket containing the Value is located.在Key上调用GetHashCode方法,定位到包含Value的bucket。

When an IEqualityComparer is passed into the constructor of a dictionary, the IEqualityComparer.Equals and IEqualityComparer.GetHashCode methods are used instead of the methods on the Key objects.当 IEqualityComparer 传递到字典的构造函数时,将使用 IEqualityComparer.Equals 和 IEqualityComparer.GetHashCode 方法而不是 Key 对象上的方法。

Now to explain why both methods are necessary, consider this example:现在要解释为什么需要这两种方法,请考虑以下示例:

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"); 

Using the BoxEqualityComparer.GetHashCode method in your example, both of these boxes have the same hashcode - 100^100^25 = 1000^1000^25 = 25 - even though they are clearly not the same object.在您的示例中使用 BoxEqualityComparer.GetHashCode 方法,这两个框具有相同的哈希码 - 100^100^25 = 1000^1000^25 = 25 - 即使它们显然不是同一个对象。 The reason that they are the same hashcode in this case is because you are using the ^ (bitwise exclusive-OR) operator so 100^100 cancels out leaving zero, as does 1000^1000.在这种情况下它们是相同的哈希码的原因是因为您使用了 ^(按位异或)运算符,因此 100^100 抵消了零,1000^1000 也是如此。 When two different objects have the same key, we call that a collision.当两个不同的对象具有相同的键时,我们称之为碰撞。

When we add two Key/Value pairs with the same hashcode to a dictionary, they are both stored in the same bucket.当我们将两个具有相同哈希码的键/值对添加到字典中时,它们都存储在同一个存储桶中。 So when we want to retrieve a Value, the GetHashCode method is called on our Key to locate the bucket.因此,当我们想要检索一个 Value 时,会在我们的 Key 上调用 GetHashCode 方法来定位存储桶。 Since there is more than one value in the bucket, the dictionary iterates over all of the Key/Value pairs in the bucket calling the Equals method on the Keys to find the correct one.由于存储桶中有多个值,字典遍历存储桶中的所有键/值对,调用键上的 Equals 方法以找到正确的值。

In the example that you posted, the two boxes are equivalent, so the Equals method returns true.在您发布的示例中,这两个框是等效的,因此 Equals 方法返回 true。 In this case the dictionary has two identical Keys, so it throws an exception.在这种情况下,字典有两个相同的键,因此会引发异常。

TLDR TLDR

So in summary, the GetHashCode method is used to generate an address where the object is stored.所以综上所述,GetHashCode 方法就是用来生成一个存放对象的地址。 So a dictionary doesn't have to search for it.所以字典不必搜索它。 It just computes the hashcode and jumps to that location.它只是计算哈希码并跳转到该位置。 The Equals method is a better test of equality, but cannot be used to map an object into an address space. Equals 方法是一种更好的相等性测试,但不能用于将对象映射到地址空间。

GetHashCode is used in Dictionary colections and it creates hash for storing objects in it. GetHashCode用于字典集合,它创建哈希以在其中存储对象。 Here is a nice article why and how to use IEqualtyComparer and GetHashCode http://dotnetperls.com/iequalitycomparer这是一篇很好的文章,为什么以及如何使用IEqualtyComparerGetHashCode http://dotnetperls.com/iequalitycomparer

While it would be possible for a Dictionary<TKey,TValue> to have its GetValue and similar methods call Equals on every single stored key to see whether it matches the one being sought, that would be very slow.虽然Dictionary<TKey,TValue>可以让它的GetValue和类似方法在每个存储的键上调用Equals以查看它是否与正在寻找的键匹配,但这会非常慢。 Instead, like many hash-based collections, it relies upon GetHashCode to quickly exclude most non-matching values from consideration.相反,像许多基于散列的集合一样,它依赖于GetHashCode来快速排除大多数不匹配的值。 If calling GetHashCode on an item being sought yields 42, and a collection has 53,917 items, but calling GetHashCode on 53,914 of the items yielded a value other than 42, then only 3 items will have to be compared to the ones being sought.如果对正在寻找的项目调用GetHashCode产生 42,并且一个集合有 53,917 个项目,但在其中的 53,914 个项目上调用GetHashCode产生的值不是 42,那么只需将 3 个项目与正在寻找的项目进行比较。 The other 53,914 may safely be ignored.其他 53,914 可以安全地忽略。

The reason a GetHashCode is included in an IEqualityComparer<T> is to allow for the possibility that a dictionary's consumer might want to regard as equal objects that would normally not regard each other as equal. GetHashCode包含在IEqualityComparer<T>是允许字典的使用者可能希望将它们视为相等的对象,而这些对象通常不会将彼此视为相等。 The most common example would be a caller that wants to use strings as keys but use case-insensitive comparisons.最常见的例子是调用者想要使用字符串作为键但使用不区分大小写的比较。 In order to make that work efficiently, the dictionary will need to have some form of hash function that will yield the same value for "Fox" and "FOX", but hopefully yield something else for "box" or "zebra".为了使其有效地工作,字典需要具有某种形式的哈希函数,该函数将为“Fox”和“FOX”产生相同的值,但希望为“box”或“zebra”产生其他值。 Since the GetHashCode method built into String doesn't work that way, the dictionary will need to get such a method from somewhere else, and IEqualityComparer<T> is the most logical place since the need for such a hash code would be very strongly associated with an Equals method that considers "Fox" and "FOX" identical to each other, but not to "box" or "zebra".由于String中内置的GetHashCode方法不能那样工作,因此字典需要从其他地方获取这样的方法,而IEqualityComparer<T>是最合乎逻辑的地方,因为对这样的哈希码的需求将非常相关使用Equals方法认为“Fox”和“FOX”彼此相同,但不是“box”或“zebra”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通用 IEqualityComparer<T> 和 GetHashCode - Generic IEqualityComparer<T> and GetHashCode IEqualityComparer的实现GetHashCode()是什么<double> - What would be implementation GetHashCode() for IEqualityComparer<double> 为什么IEqualityComparer <T> 有GetHashCode()方法? - Why IEqualityComparer<T> has GetHashCode() method? 利用IEqualityComparer的GetHashCode()部分 <T> 直接比较? - Utilizing the GetHashCode() part of IEqualityComparer<T> for direct comparisons? 为IEqualityComparer实现GetHashCode <T> 条件平等 - Implementing GetHashCode for IEqualityComparer<T> with conditional equality 什么等效于.Net在PHP中的GetHashCode()? - What is the equivalent of .Net's GetHashCode() in PHP? 实施IEqualityComparer时 <T> .GetHashCode(T obj),我可以使用当前实例的状态,还是必须使用obj? - When implementing IEqualityComparer<T>.GetHashCode(T obj), can I use the current instance's state, or do I have to use obj? 使用IEqualityComparer和Equals / GethashCode Override有什么区别? - What is the difference between using IEqualityComparer and Equals/GethashCode Override? 使用具有公差的IEqualityComparer GetHashCode - Using IEqualityComparer GetHashCode with a tolerance 使用IEqualityComparer <T>的推荐最佳做法是什么? - What's the recommended best practice for using IEqualityComparer<T>?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM