c# - 使用 Dictionary 和 HashSet 的 GetHashCode 方法

标签 c# .net dictionary hashset

我有一个关于 Dictionary 和 HashSet 在 C# 中如何工作的问题。根据我的理解,GetHashCode用于哈希表中来确定键的唯一性。

在以下 MSDN 页面上,它指出:

A hash code is a numeric value that is used to insert and identify an object in a hash-based collection such as the Dictionary class, the Hashtable class, or a type derived from the DictionaryBase class.

链接: MSDN Object.GetHashCode

如果是这样,为什么当 car2 具有与 car1 相同的哈希码时,ContainsKey 和 Contains 会返回 false?如果我的理解是正确的,如果 MSDN 所说的是正确的,那么两者不应该都返回 true 吗?

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;
    }
}

最佳答案

因为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;
}

GetHashCode 所做的只是获取您的 key 将放入的存储桶,它仍然必须遍历该存储桶的每个成员并通过 Equals 方法找到精确匹配。这就是为什么拥有良好的哈希码很重要的原因,桶中的项目越少,foreach 部分就会越快。最好的哈希码每个桶只有一个项目。


这是 actual code for Contains on a HashSet(Dictionary 的 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
}

关于c# - 使用 Dictionary 和 HashSet 的 GetHashCode 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28593764/

相关文章:

c# - 数据 block 中的异常处理

c# - Linq Groupby、Union 和 Sum 聚合多个集合中的数据

c# - 如何将这些代码更改为 C# 样式?

c# - 如何在我的自定义类上使用 IDisposable?

c# - 无法从 Dictionary 转换为 IDictionary

javascript - 为什么我不能迭代我的 ES6 Map 的属性?

c# - 将 Identity 2.0 抽象到领域模型层

c# - 使用 OpenXML 将 Excel 2013 范围格式化为表格

c# - 一种根据另一种风格激活的风格?

python:动态获取字典中的子字典?