用于字节数组的HashSet

时间:2018-04-09 18:53:00

标签: c# .net

我需要一个字节数组的HashSet来检查集合中是否存在给定的字节数组。但似乎这对字节数组(或任何数组)都不起作用。

这是我的测试代码:

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

有没有办法为字节数组创建一个HashSet?

1 个答案:

答案 0 :(得分:2)

使用HashSet构建IEqualityComparer<byte[]>。你不想在这里使用界面。虽然byte[]实际上实现了IEnumerable<byte>IList<byte>等接口,但由于涉及重量,使用它们是一个坏主意。你没有使用string实施IEnumerable<char>的事实,所以不要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();
}

https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx

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

与字节数组相比,列表往往只有少数元素,因此常常是简单的哈希函数,如foreach (var item in list) hashcode = hashcode * 5 + item.GetHashCode();如果你对字节数组使用那种哈希函数,你会遇到问题。乘以一个小的奇数技巧最终会变得相当偏倚,以至于舒适。我在这里给出的特定哈希函数可能不是最优的,但是我们已经对这个系列进行了测试,并且它在三百万个条目中运行良好。由于拥有大量只有两个字节长/不同的冲突,乘法很快就陷入了麻烦。如果你避免使用简并数,这个系列就不会在两个字节中发生冲突,而且大多数都没有三个字节的冲突。

考虑实际使用情况:到目前为止,这里最常见的两件事是字节字符串,并检查实际文件的相同性。在任何一种情况下,获取前几个字节的哈希码很可能是个坏主意。 String的哈希码使用整个字符串,因此字节字符串应该相同,并且大多数重复的文件在前几个字节中没有唯一的前缀。对于N个条目,如果N上的平方根有哈希冲突,那么在生成哈希码时你也可能已经遍历了整个数组,忽略了比较比哈希更慢的事实。