我们什么时候为Dictionary做GetHashCode()?

我已将Dictionary(TKey,TValue)用于多种用途。 但我没有遇到任何实现GetHashCode()的场景,我认为这是因为我的键是主要类型,如int和string。 我很想知道场景(真实世界的例子),当一个人应该使用自定义对象的键,从而实现方法GetHashCode()Equals()等。

并且,使用自定义对象作为密钥是否需要实现这些function?

每当默认的Object.Equals (测试参考相等)不够时,您应该重写EqualsGetHashCode 。 例如,当您的密钥类型是自定义类型并且您希望两个密钥被视为相等时,即使在它们不是自定义类型的同一实例的情况下,也会发生这种情况。

例如,如果您的密钥非常简单

 class Point { public int X { get; set; } public int Y { get; set; } } 

并且如果它们的X s相等并且它们的Y s相等则你想要两个Point s 2被认为是相等的那么你将需要重写EqualsGetHashCode

只是为了说清楚: DictionaryGetHashCode()有一个重要的事情:Dictionary使用GetHashCode来确定两个键是否相等,即如果是自定义类型,你应该关心实现GetHashCode()小心。 正如Andrew Hare指出这很容易,如果你有一个简单的类型,可以明确地识别你的自定义对象。 如果你有一个组合标识符,它会变得有点复杂。

例如,将复数视为TKey 。 复数由它的真实及其虚部决定。 两者都是简单类型,例如double 。 但是,如果两个复数相等,你会如何确定? 您为自定义复杂类型实现GetHashode()并组合两个标识部分。

你会在这里找到关于后者的进一步阅读。

UPDATE

基于Ergwun的评论,我检查了Dictionary.Add的行为,特别关注TKeyEquals(object)GetHashCode() 。 我必须承认我对结果感到非常惊讶。

给定TKey类型的两个对象k1k2TValue类型的两个任意对象v1v2 ,以及Dictionary类型的空字典d ,这是将v1与键k1首先添加到d时发生的情况和v2与key k2秒(取决于TKey.Equals(object)TKey.GetHashCode() ):

 k1.Equals(k2) k1.GetHashCode() == k2.GetHashCode() d.Add(k2, v2) false false ok false true ok true false ok true true System.ArgumentException 

结论:我错了,因为我最初认为第二种情况(其中Equals返回false但两个关键对象具有相同的哈希代码)会引发ArgumentException 。 但是,由于第三种情况以某种方式显示字典确实使用GetHashCode() 。 无论如何,两个相同类型且相同的对象必须返回相同的哈希码以确保实例Dictionary正常工作似乎是一个好建议。

一个例子是当你需要创建一个复合键(这是一个由多个数据组成的键)。 该组合键将是需要覆盖这些方法的自定义类型。

例如,假设您有一个内存中的地址记录缓存,并且您想检查地址是否在缓存中以节省昂贵的数据库访问以检索它。 我们还要说地址在街道1邮政编码字段方面是独一无二的。 您可以使用以下内容实现缓存:

 class AddressCacheKey { public String StreetOne { get; set; } public String ZipCode { get; set; } // overrides for Equals and GetHashCode } 

 static Dictionary cache; 

由于您的AddressCacheKey类型会覆盖EqualsGetHashCode方法,因此它们可以很好地替换字典中的键,您可以确定是否需要访问数据库以检索基于多个的记录一块数据。

你有两个问题。

  1. 什么时候需要实现GetHashCode()
  2. 你会用一个对象作为字典键吗?

让我们从1.开始。如果你正在编写一个可能被其他人使用的类,你将需要定义GetHashCode()和Equals(),当引用Equals()不够时。 如果您不打算在字典中使用它,并且它是为了您自己的用法,那么我认为没有理由跳过GetHashCode()等。

对于2),您应该在需要从对象到其他类型的常量时间查找时使用对象。 由于GetHashCode()返回一个数值,并且集合存储引用,因此在Int或字符串上使用Object没有任何代价(请记住字符串是一个对象)。