C# 带有字典和哈希集的GetHashCode方法
我有一个关于C#中字典和哈希集如何工作的问题。根据我的理解,在哈希表中使用GetHashCode来确定键的唯一性 在以下MSDN页面上,它指出: 哈希代码是一个数值,用于在基于哈希的集合(如字典类、哈希表类或从DictionaryBase类派生的类型)中插入和标识对象 链接: 如果是这种情况,为什么ContainsKey和Contains在car2的哈希代码与car1相同时返回false?如果我的理解是正确的,如果MSDN所说的是正确的,那么这两种说法不都是正确的吗C# 带有字典和哈希集的GetHashCode方法,c#,.net,dictionary,hashset,C#,.net,Dictionary,Hashset,我有一个关于C#中字典和哈希集如何工作的问题。根据我的理解,在哈希表中使用GetHashCode来确定键的唯一性 在以下MSDN页面上,它指出: 哈希代码是一个数值,用于在基于哈希的集合(如字典类、哈希表类或从DictionaryBase类派生的类型)中插入和标识对象 链接: 如果是这种情况,为什么ContainsKey和Contains在car2的哈希代码与car1相同时返回false?如果我的理解是正确的,如果MSDN所说的是正确的,那么这两种说法不都是正确的吗 class Program
class Program
{
static void Main(string[] args)
{
// Create a Dictionary and HashSet
Dictionary<Car, int> carDictionary = new Dictionary<Car, int>();
HashSet<Car> carSet = new HashSet<Car>();
// Create 3 Cars (2 generic and 1 Civic)
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Civic();
// Test hash values
int test1 = car1.GetHashCode(); // 22008501
int test2 = car2.GetHashCode(); // 22008501
int test3 = car3.GetHashCode(); // 12048305
// Add 1 generic car and 1 Civic to both Dictionary and HashSet
carDictionary.Add(car1, 1);
carDictionary.Add(car3, 1);
carSet.Add(car1);
carSet.Add(car3);
// Why are both of these false?
bool dictTest1 = carDictionary.ContainsKey(car2); // false
bool setTest1 = carSet.Contains(car2); // false
// Testing equality makes sense
bool testA = car1.Equals(car2); // false
bool testB = car1.Equals(car3); // false
}
}
class Car
{
public override int GetHashCode()
{
return 22008501;
}
}
class Civic : Car
{
public override int GetHashCode()
{
return 12048305;
}
}
类程序
{
静态void Main(字符串[]参数)
{
//创建字典和哈希集
字典carDictionary=新字典();
HashSet carSet=新HashSet();
//创建3辆车(2辆普通车和1辆思域车)
Car car1=新车();
Car car2=新车();
Car car3=新思域();
//测试散列值
int test1=car1.GetHashCode();//22008501
int test2=car2.GetHashCode();//22008501
int test3=car3.GetHashCode();//12048305
//向Dictionary和HashSet添加1个generic car和1个Civic
添加(car1,1);
添加(car3,1);
carSet.Add(car1);
carSet.Add(car3);
//为什么这两个都是假的?
bool dictTest1=carDictionary.ContainsKey(car2);//false
bool setTest1=carSet.Contains(car2);//false
//测试平等性是有意义的
bool testA=car1.Equals(car2);//false
bool testB=car1.Equals(car3);//false
}
}
班车
{
公共覆盖int GetHashCode()
{
返回22008501;
}
}
类别:私家车
{
公共覆盖int GetHashCode()
{
返回12048305;
}
}
不必保证哈希代码是唯一的,如果键相等,则它们必须相等
现在发生的是,这些项目存储在桶中。如果查询字典
是否包含给定的键或哈希集
给定的项,它将首先计算哈希码以获取正确的bucket
接下来,它将对bucket中的所有项进行迭代,并对其执行.Equals
测试。只有在其中一个匹配的情况下,才会返回true
换句话说,允许一个为每个实例返回相同的hashcode,尽管实例不同。这只会使散列效率低下
因此,C#存储一个字典
,如:
+----------+
| 22008501 |---<car1,1>----<car3,1>----|
+----------+
| 11155414 | (other bucket)
+----------+
+----------+
| 22008501 |-----------|
+----------+
|11155414 |(其他铲斗)
+----------+
左侧有(可能的bucket标签),尽管对于较小的字典
,bucket的数量将非常小,并且将对散列(例如模)执行操作,以使结果的数量更小
现在,如果您查询
car2
是否在字典中,它将计算哈希值,从而获取第一个bucket。然后它将迭代,并对car1
vscar2
执行相等检查,然后car3
vscar2
,它将到达存储桶的末尾并返回false
。这是因为默认的Equals
操作是reference equality。只有当您也覆盖了(例如,所有汽车都是相同的,您可以返回true
)。正如您所注意到的,car1.Equals(car2)
不是真的Dictionary
和Hashset
成员资格仅适用于相等的对象。这意味着.Equals()
返回true。只有当它们的hashcode第一次被发现相等时,才会对其进行测试。,因为ContainsKey的逻辑与此类似
//This is a simplified model for answering the OP's question, the real one is more complex.
private List<List<KeyValuePair<TKey,TValue>>> _buckets = //....
public bool ContainsKey(TKey key)
{
List<KeyValuePair<TKey,TValue>> bucket = _buckets[key.GetHashCode() % _buckets.Length];
foreach(var item in bucket)
{
if(key.Equals(item.Key))
return true;
}
return false;
}
这与哈希冲突有关吗?它们必须具有相同的哈希代码并且相等。您还应该覆盖object.Equals
。您可能有兴趣查看Jon Skeet的答案。这可以回答您的问题:谢谢,这为我澄清了问题。我明白了。我理解equality方法,但不理解字典和HashSet是如何实现Contains/ContainsKey方法的。正确的实现可在的参考源页面中找到。
private int[] m_buckets;
private Slot[] m_slots;
public bool Contains(T item) {
if (m_buckets != null) {
int hashCode = InternalGetHashCode(item);
// see note at "HashSet" level describing why "- 1" appears in for loop
for (int i = m_buckets[hashCode % m_buckets.Length] - 1; i >= 0; i = m_slots[i].next) {
if (m_slots[i].hashCode == hashCode && m_comparer.Equals(m_slots[i].value, item)) {
return true;
}
}
}
// either m_buckets is null or wasn't found
return false;
}
private int InternalGetHashCode(T item) {
if (item == null) {
return 0;
}
return m_comparer.GetHashCode(item) & Lower31BitMask;
}
internal struct Slot {
internal int hashCode; // Lower 31 bits of hash code, -1 if unused
internal T value;
internal int next; // Index of next entry, -1 if last
}