C# 字节数组的哈希集

C# 字节数组的哈希集,c#,.net,C#,.net,我需要字节数组的哈希集,以便检查集合中是否存在给定的字节数组。但这似乎不适用于字节数组(或者任何数组) 以下是我的测试代码: void test() { byte[] b1 = new byte[] { 1, 2, 3 }; byte[] b2 = new byte[] { 1, 2, 3 }; HashSet<byte[]> set = new HashSet<byte[]>(); set.Add(b1); set.Add(b2

我需要字节数组的哈希集,以便检查集合中是否存在给定的字节数组。但这似乎不适用于字节数组(或者任何数组)

以下是我的测试代码:

void test()
{
    byte[] b1 = new byte[] { 1, 2, 3 };
    byte[] b2 = new byte[] { 1, 2, 3 };

    HashSet<byte[]> set = new HashSet<byte[]>();
    set.Add(b1);
    set.Add(b2);
    Text = set.Count.ToString();//returns 2 instead of the expected 1.
}
void测试()
{
字节[]b1=新字节[]{1,2,3};
字节[]b2=新字节[]{1,2,3};
HashSet=newhashset();
增加(b1);
增加(b2);
Text=set.Count.ToString();//返回2而不是预期的1。
}

有没有办法为字节数组创建哈希集?

IEqualityComparer
构造一个
HashSet
。您不想在这里使用接口。虽然
byte[]
实际上实现了诸如
IEnumerable
IList
等接口,但由于涉及的重要性,使用它们是一个坏主意。您根本不需要使用
string
实现
IEnumerable
这一事实,因此也不要使用
byte[]
实现

public class bytearraycomparer : IEqualityComparer<byte[]> {
    public bool Equals(byte[] a, byte[] b)
    {
        if (a.Length != b.Length) return false;
        for (int i = 0; i < a.Length; i++)
            if (a[i] != b[i]) return false;
        return true;
    }
    public int GetHashCode(byte[] a)
    {
        uint b = 0;
        for (int i = 0; i < a.length; i++)
            b = ((b << 23) | (b >> 9)) ^ a[i];
        return unchecked((int)b);
    }
}

void test()
{
    byte[] b1 = new byte[] { 1, 2, 3 };
    byte[] b2 = new byte[] { 1, 2, 3 };

    HashSet<byte[]> set = new HashSet<byte[]>(new bytearraycomparer );
    set.Add(b1);
    set.Add(b2);
    Text = set.Count.ToString();
}
公共类bytearraycomparer:IEqualityComparer{
公共布尔等于(字节[]a,字节[]b)
{
如果(a.Length!=b.Length)返回false;
for(int i=0;i9))^a[i];
未选中返回((int)b);
}
}
无效测试()
{
字节[]b1=新字节[]{1,2,3};
字节[]b2=新字节[]{1,2,3};
HashSet set=newhashset(newbytearraycomparer);
增加(b1);
增加(b2);
Text=set.Count.ToString();
}

如果您要在建议的重复问题中使用答案,那么每处理一个字节,您将得到一个函数调用和一个数组边界检查。你不会想要的。如果以这样最简单的方式表示,抖动将内联抓取,然后注意边界检查不能失败(数组不能调整大小)并忽略它们。整个阵列只有一个函数调用。耶

与字节数组相比,列表往往只有几个元素,因此通常使用简单的哈希函数,例如
foreach(列表中的var项)hashcode=hashcode*5+item.GetHashCode()
;如果对字节数组使用这种散列函数,就会出现问题。乘以一个小奇数的技巧最终会很快产生偏差,让人感到不舒服。我在这里给出的特定散列函数可能不是最优的,但我们已经在这个系列上运行了测试,它在300万个条目中运行得非常好。由于存在大量只有两个字节长/不同的冲突,因此乘奇数运算太快陷入麻烦。如果避免退化数字,则此族在两个字节内不会发生冲突,而大多数在三个字节内不会发生冲突


考虑实际用例:到目前为止,这里最有可能的两件事是检查字节字符串和实际文件是否相同。在这两种情况下,采用前几个字节的哈希代码很可能是个坏主意
String
的哈希代码使用整个字符串,因此字节字符串也应该这样做,并且大多数被复制的文件在前几个字节中没有唯一的前缀。对于N个条目,如果N上的平方根存在哈希冲突,那么在生成哈希代码时,您最好遍历整个数组,忽略比较比哈希慢的事实。

您将两个不同的数组添加到
哈希集
,尽管它们的内容相同。您需要使用一个比较内容的
EqualityComparer
HashSet
调用
T.Equals()
来确定元素是否相等,
Array.Equals()
返回引用是否相等,因此
b1.Equals(b1)
为true,而
b1.Equals(b2)
为false。您应该创建一个类并实现
Equals
。好的哈希函数取决于您期望的数组。假设您需要少量数组,每个数组都很长。然后,通常提出的对数组中的所有字节执行某些操作的散列函数是非常糟糕的,您最好使用简单的散列函数,甚至像数组这样的小散列函数。在这种情况下,长度可能更好。@Servy:我不想使用链接的答案。虽然它可以工作,但字节数组的分布与其他类型的列表有很大的不同。如果发生这种情况,您将随机数组存储在那里(随机我的意思是分布几乎是一致的)-然后取前4个字节,转换为int(使用位转换器),并将其用作哈希代码,因为对于随机数组来说,冲突的可能性可以忽略不计,并且不会浪费cpu在整个数组中循环2次。谢谢+1.如何选择
GetHashCode
实现?这在
HashSet
@ispiro中非常重要:我输入了一个类似于ELFHash的ok散列代码。如果你想问一个好的问题,可以问另一个问题。@Joshua:我对你的散列计算很着迷。这是一个标准的遵循或提出了自己的版本?不管怎样+1@ispiro:谢谢。我在posted中没有找到Joshua的版本化哈希实现link@AkashKC:想法是在不饱和的情况下平滑混合位(奇数相乘的传统智慧偏向于1位)。8到24之间的任何数字,如果是32的素数,都可以。