索引对象的哈希函数

时间:2015-01-09 20:08:44

标签: c# hashset gethashcode

说,我有一个类,它从0,...,n-1(使用创建对象的静态计数器)索引从它创建的所有对象。由于这些对象在HashSets和Dictionaries中使用,我们需要一个Hash函数。

有没有理由不将此索引用作哈希值?

3 个答案:

答案 0 :(得分:3)

你当然可以使用它,但如果你这样做,那就意味着每个单独的对象实例被那些基于散列的结构视为不同的对象。如果您希望能够考虑不同的对象实例,那么#34;相等"那么这种方法就行不通了。

如果这实际上是你的目标,那么根本没有理由覆盖默认的相等/哈希码语义。默认实现将比较对象引用,导致每个对象“不同”#34;来自其他所有对象。因此,请节省您自己的努力,而不必费心去做任何事情

答案 1 :(得分:1)

以下是HashSet上包含的actual code

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
}

您要注意的关键事项是调用GetHashCode(),然后对结果执行hashCode % m_buckets.Length以确定m_slots中存储的单个链接列表根目录应该遍历。

最佳算法将为您提供hashCode % m_buckets.Length之间均匀的值分布,因此所有链接列表的长度都相同。从0开始并且向上计数完美地完成了这一点,所以是的,如果你能为一个唯一的对象获得一个固定的索引并且只计算一个完美的哈希码。

答案 2 :(得分:0)

不使用索引作为哈希函数的一个原因是因为想要在不同的实例中重复

假设您在实体系统中使用Dictionaty,并且您的密钥是任何给定组件的实体和组件类型的组合。在查找组件时,您希望能够从实体,组件类型创建新密钥,并使其等同于具有相同实体和组件类型的密钥。通过这种方式,静态递增索引不是可行的方法,因为它会导致表示具有不同HashCode的相同值的对象,从而导致它作为Dictionary中的键无用。

另一个原因是,对于在具有延长生命周期的程序中运行的类型,您可能拥有任意数量的对象 - 让我们说数据库驱动程序上的事务管理器。在这种情况下,实际上可能会用完整数值(如果允许使用负数或使用uint,则约为42亿个值)。在这种情况下,哈希码不足以支持唯一性 - 这是哈希码的正常行为,但是过度优化的可能性很小。