为BitArray生成良好的哈希码(GetHashCode)

时间:2010-06-26 22:15:43

标签: c# .net dictionary gethashcode bitarray

我需要在GetHashCode中为BitArray生成快速哈希码。我有一个字典,其中键是BitArrays,所有的BitArrays都是相同的长度。

有没有人知道从可变位数生成良好哈希的快速方法,如在这种情况下?

更新

我最初采用的方法是直接通过反射访问内部的int数组(速度比这种情况下的封装更重要),然后对这些值进行异或。 XOR方法似乎运作良好,即在字典中搜索时,我的“等于”方法不会过度调用:

    public int GetHashCode(BitArray array)
    {
        int hash = 0;
        foreach (int value in array.GetInternalValues())
        {
            hash ^= value;
        }
        return hash;
    }

然而,Mark Byers建议并在StackOverflow其他地方看到的方法略好一些(对于我的测试数据,对于XOR,16570等于呼叫对比16608)。请注意,此方法修复了前一个错误,其中超出位数组末尾的位可能会影响散列值。如果位数组的长度减少,则可能发生这种情况。

    public int GetHashCode(BitArray array)
    {
        UInt32 hash = 17;
        int bitsRemaining = array.Length;
        foreach (int value in array.GetInternalValues())
        {
            UInt32 cleanValue = (UInt32)value;
            if (bitsRemaining < 32)
            {
                //clear any bits that are beyond the end of the array
                int bitsToWipe = 32 - bitsRemaining;
                cleanValue <<= bitsToWipe;
                cleanValue >>= bitsToWipe;
            }

            hash = hash * 23 + cleanValue;
            bitsRemaining -= 32;
        }
        return (int)hash;
    }

GetInternalValues扩展方法实现如下:

public static class BitArrayExtensions
{
    static FieldInfo _internalArrayGetter = GetInternalArrayGetter();

    static FieldInfo GetInternalArrayGetter()
    {
        return typeof(BitArray).GetField("m_array", BindingFlags.NonPublic | BindingFlags.Instance);
    }

    static int[] GetInternalArray(BitArray array)
    {
        return (int[])_internalArrayGetter.GetValue(array);
    }

    public static IEnumerable<int> GetInternalValues(this BitArray array)
    {
        return GetInternalArray(array);
    }

... more extension methods
}

欢迎任何改进建议!

2 个答案:

答案 0 :(得分:3)

作为词典中的关键词,这是一个可怕的课程。实现GetHashCode()的唯一合理方法是使用其CopyTo()方法将位复制到byte []中。这不是很好,它会产生大量的垃圾。

请求,窃取或借用以使用BitVector32。它有一个很好的GetHashCode()实现。如果你有超过32位,那么考虑转动你自己的类,这样就可以在不必复制的情况下进入底层数组。

答案 1 :(得分:1)

如果位数组是32位或更短,那么你只需要将它们转换为32位整数(必要时用零位填充)。

如果它们可以更长,那么你可以将它们转换为一系列32位整数并对它们进行异或,或者更好:使用Effective Java中描述的算法。

public int GetHashCode()
{
    int hash = 17;
    hash = hash * 23 + field1.GetHashCode();
    hash = hash * 23 + field2.GetHashCode();
    hash = hash * 23 + field3.GetHashCode();
    return hash;
}

取自here。 field1,field2对应前32位,后32位等