C# 为位数组生成良好的哈希代码(GetHashCode)

C# 为位数组生成良好的哈希代码(GetHashCode),c#,.net,dictionary,gethashcode,bitarray,C#,.net,Dictionary,Gethashcode,Bitarray,我需要在GetHashCode中为位数组生成一个快速哈希代码。我有一个字典,其中键是位数组,所有位数组的长度都相同 有没有人知道一种从可变位数生成好的散列的快速方法,就像在这个场景中一样 更新: 我最初采用的方法是通过反射直接访问int的内部数组(在本例中,速度比封装更重要),然后对这些值进行异或。XOR方法似乎运行良好,即在字典中搜索时不会过度调用我的“Equals”方法: public int GetHashCode(BitArray array) { int

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

有没有人知道一种从可变位数生成好的散列的快速方法,就像在这个场景中一样

更新:

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

    public int GetHashCode(BitArray array)
    {
        int hash = 0;
        foreach (int value in array.GetInternalValues())
        {
            hash ^= value;
        }
        return hash;
    }
然而,MarkByers建议的方法和StackOverflow上其他地方看到的方法稍好一些(16570等于调用,而我的测试数据的XOR为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;
    }
public int GetHashCode(位数组)
{
UInt32散列=17;
int bitsRemaining=array.Length;
foreach(数组中的int值。GetInternalValues())
{
UInt32清洁值=(UInt32)值;
如果(位保留<32)
{
//清除数组末尾以外的所有位
int bitsToWipe=32-位保留;
cleanValue=比特斯托管道;
}
hash=hash*23+cleanValue;
位保留-=32;
}
返回(int)散列;
}
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
}
公共静态类BitArrayExtensions
{
静态字段信息_internalArrayGetter=GetInternalArrayGetter();
静态字段信息GetInternalArrayGetter()
{
返回typeof(BitArray).GetField(“m_array”,BindingFlags.NonPublic | BindingFlags.Instance);
}
静态int[]GetInternalArray(位数组)
{
return(int[])_internalArrayGetter.GetValue(数组);
}
公共静态IEnumerable GetInternalValues(此位数组)
{
返回GetInternalArray(数组);
}
…更多扩展方法
}

欢迎提出任何改进建议

如果位数组为32位或更短,则只需将其转换为32位整数(必要时用零位填充)

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

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

摘自。字段1、字段2对应前32位、后32位等。

在字典中充当键是一个糟糕的类。实现GetHashCode()的唯一合理方法是使用其CopyTo()方法将位复制到字节[]。这不太好,它会产生大量垃圾


乞求、窃取或借用比特向量32。它为GetHashCode()提供了一个很好的实现。如果你有超过32位,然后考虑旋转你自己的类,这样你就可以在不必复制的情况下进入基础数组。

我已经在别处提到了你的方法,但是我并不真正理解它背后的理论或者选择“魔法”素数。这种方法比我最初采用的XOR方法稍微有效一些(16570等于调用,16608等于测试数据的XOR)。有关详细信息,请参阅我的编辑。我需要超过32位。我曾考虑编写自己的类(在Reflector的帮助下),但不利用内置的BitArray似乎很遗憾。一点反射黑客让我得到了内部阵列,当然在未来版本的框架中可能会发生变化-例如,64位版本在64位硬件上可能更有效。不过,我现在对这个解决方案很满意。