我试图理解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();
    }
}

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

谢谢

卢西安

推荐答案

先了解一下背景知识……

里面的每一个物体.NET有一个Equals方法和一个GetHashCode方法.

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

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

字典是一种非常酷的数据 struct ,它以更高的内存占用换取(或多或少)添加/删除/获取操作的固定成本.不过,对于迭代来说,这是一个糟糕的 Select .在内部,字典包含一个存储桶数组,可以在其中存储值.向字典添加键和值时,会对键调用GetHashCode方法.返回的哈希代码用于确定存储密钥/值对的存储桶的索引.

当您想要访问该值时,请再次传入密钥.对键调用GetHashCode方法,并找到包含该值的bucket.

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

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

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

使用BoxEqualityComparer.GetHashCode方法在您的示例中,这两个框都有相同的hashcode-100^100^25=1000^1000^25=25,尽管它们显然不是同一个对象.在本例中,它们是相同的哈希代码的原因是,您使用的是^(按位异或)运算符,因此100^100会取消保留零,1000^1000也是如此.当两个不同的对象具有相同的键时,我们称之为碰撞.

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

在您发布的示例中,这两个框是等效的,因此Equals方法返回true.在本例中,字典有两个相同的键,因此会引发异常.

TLDR

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

.net相关问答推荐

ECS服务无法从Cognito获取配置

Visual Studio 2022 中的目标操作系统和目标运行时有什么区别?

CustomControl 计算自己的宽度和高度 - 使用 bounds.Height=0 调用 ArrangeOverride

C# 中的批量更新

标签从右向左增长

Environment.TickCount 与 DateTime.Now

LINQ:确定两个序列是否包含完全相同的元素

如何 Select 数据表中列的最小值和最大值?

如何使用c#从excel文件中读取数据

如何创建只读依赖属性?

使用 .NET 中的代码更改桌面墙纸

新的 netstandardapp 和 netcoreapp TFM 有什么区别?

为什么使用 ImmutableList 而不是 ReadOnlyCollection?

如何制作通用类型转换函数

如何在 C# 中以编程方式安装 Windows 服务?

无法加载文件或程序集System.ValueTuple

通过反射查找可空属性的类型

C# 相当于 Java 的 Exception.printStackTrace()?

当它被抛出和捕获时,不要在那个异常处停止调试器

实体框架太慢了.我有哪些 Select ?