我有一个类型,我在IDictionary中使用它作为键。类型如下
public class Employee
{
public string Name { get; set; }
public int ID { get; set; }
public override bool Equals(object obj)
{
Employee emp = obj as Employee;
if (emp != null)
return emp.Name.Equals(this.Name);
return false;
}
public override int GetHashCode()
{
return this.Name.GetHashCode();
}
}
现在我已经在我的主要内容中创建了一个字典,如下所示
IDictionary<Employee, int> empCollection = new Dictionary<Employee, int>();
Employee emp1 = new Employee() { Name = "abhi", ID = 1 };
Employee emp2 = new Employee() { Name = "vikram", ID = 2 };
Employee emp3 = new Employee() { Name = "vikram", ID = 3 };
empCollection.Add(emp1, 1);
empCollection.Add(emp2, 2);
empCollection.Add(emp3, 3);
现在在调试时我发现当emp1被添加到集合中时,只调用了GetHashCode方法的密钥类型,之后当emp2被添加到集合时,再次调用GetHashCode方法但是在emp3的情况下都是GetHashCode调用Equals方法。
在问这个问题时可能看起来太天真但是为什么当eqImp2对象被添加到集合时没有调用Equals方法。里面发生了什么。请解释一下。
答案 0 :(得分:8)
字典和所有其他类似容器使用哈希码作为快速和脏检查:不同的哈希码意味着两个对象肯定不相等;相同的哈希码并不意味着什么。 GetHashCode
的文档通过说
如果两个对象比较相等,则每个对象的GetHashCode方法 对象必须返回相同的值。但是,如果两个对象没有 比较相同,两个对象的GetHashCode方法不相同 必须返回不同的值。
您的emp1
和emp2
会生成不同的哈希码,因此字典无需运行Equals
;它已经知道它们并不平等。另一方面,emp2
和emp3
生成相同的哈希码,因此字典必须调用Equals
来明确确定它们是否确实相等,或者相同的哈希码只是偶然的结果
答案 1 :(得分:1)
在您的示例中,GetHashCode
查看Name哈希码。 emp3与emp2同名,(“vikram”)。它们在给定哈希码的情况下是相等的,因此它使用Equals
进一步查看。
答案 2 :(得分:1)
emp2和emp3具有相同的密钥。这将导致字典中的“键冲突”。它首先调用GetHashCode()并确定哈希码是相同的。然后通过调用Equals()确保它们是相同的。 Dictionary中的代码是:
int num = this.comparer.GetHashCode(key) & 2147483647;
...
if (this.entries[i].hashCode == num && this.comparer.Equals(this.entries[i].key, key))
显然,如果哈希码不匹配,则永远不必调用Equals。
你应该得到像ILSpy这样的工具,然后你可以查看代码并自己找到答案。
答案 3 :(得分:1)
如果您继续此实验,您会发现某些特定于Dictionary<TKey, TValue>
实施的行为,以及由于您实施GetHashCode
的方式而需要的一些行为。
首先,在比较对象是否相等时,了解GetHashCode
和Equals
的作用非常重要。有关其他信息,请参见this question,但我将在此重复基本规则:
Equals
方法确切地确定哪些对象相等以及哪些对象不相等。在返回之前,需要在此方法中执行所有必要的检查以进行最终确定。Equals
肯定会返回false),但相等的哈希码并不意味着什么(即Equals
可以返回true或者是假的。将值与密钥对象(例如,.NET中的IDictionary<TKey, TValue>
或Java中的Map<K, V>
)相关联的集合利用哈希代码来提高实现效率。但是,由于Object.GetHashCode
的文档特别不要求结果是唯一的,因此这些集合不能单独依赖哈希码来获得正确的功能。 当两个对象具有相同的哈希码时,只有对Equals
的调用才能区分它们。您为emp3
插入所描述的案例属于这种情况:[{如果您尝试插入相同的值,则{1}}]方法需要抛出IDictionary<TKey, TValue>.Add
,并且只有对ArgumentException
的调用才能确定新密钥Equals
是否等于之前已插入emp3
。
其他实施特征
特定的集合实现可能会导致对emp3
的调用次数超出预期。例如,当调整hash table的内部存储大小时,实现可能会为集合中存储的每个对象调用GetHashCode
。基于binary-或B-tree的集合可能只调用GetHashCode
一次(如果结果缓存在树结构中),或者可能需要在多个对象期间调用GetHashCode
每次插入或查找操作(如果结果未缓存)。
有时哈希表实现需要为多个对象调用GetHashCode
,或者甚至为具有不同哈希码的对象调用GetHashCode
,因为他们使用模运算将键放入“桶”。具体特征因实施而异。
答案 4 :(得分:0)
这是因为 GetHashCode 是一种捷径。 C#将首先调用 GetHashCode ,它应该是快速执行的。 如果两个对象具有不同的HashCodes,那么就不需要调用更为昂贵的 Equals 方法。 只有当他们拥有相同的HashCode时,它才会调用 Equals 。这是因为HashCode不能保证是唯一的