适用于字节数组的哈希码方法是什么?

9

什么是适用于byte数组的最佳哈希方法?

这些数组是包含jpeg图像的序列化类对象,在TCP/IP应用程序之间传递。

数组大小约为200k。

4个回答

12

任何内置的哈希函数都可以使用;根据您对冲突的在意程度,以下是您的选择(从最多冲突到最少):

  • MD5
  • SHA1
  • SHA256
  • SHA384
  • SHA512

它们的使用方法很简单,如下所示:

var hash = SHA1.Create().ComputeHash(data);

额外加分:如果你不关心安全(考虑到你正在获取图像的哈希值,我认为你并不关心),你可能想考虑Murmur哈希,它是用于内容哈希而不是安全哈希的(因此速度更快)。然而,它不在框架中,因此您需要找到实现(并且您应该选择Murmur3)。

编辑:如果您正在寻找byte[]数组的HASHCODE,那完全取决于您自己,它通常由位移(通过质数)和XORing组成。例如:

public class ByteArrayEqualityComparer : IEqualityComparer<byte[]>
{
    public static readonly ByteArrayEqualityComparer Default = new ByteArrayEqualityComparer();
    private ByteArrayEqualityComparer() { }

    public bool Equals(byte[] x, byte[] y)
    {
        if (x == null && y == null)
            return true;
        if (x == null || y == null)
            return false;
        if (x.Length != y.Length)
            return false;
        for (var i = 0; i < x.Length; i++)
            if (x[i] != y[i])
                return false;
        return true;
    }

    public int GetHashCode(byte[] obj)
    {
        if (obj == null || obj.Length == 0)
            return 0;
        var hashCode = 0;
        for (var i = 0; i < obj.Length; i++)
            // Rotate by 3 bits and XOR the new value.
            hashCode = (hashCode << 3) | (hashCode >> (29)) ^ obj[i];
        return hashCode;
    }
}
// ...
var hc = ByteArrayEqualityComparer.Default.GetHashCode(data);

编辑: 如果您想要验证值是否已更改,则应使用CRC32


谢谢您的回答,我只需要快速比较byte[]数组内容,不需要加密哈希。我需要确保发送的数据在接收端保持不变。 - Chesnokov Yuriy
@Chesnokov,那你为什么不一开始就问呢? - Jonathan Dickinson
我指的是通过哈希值进行比较,就像问题中所述,数据随着哈希一起通过互联网发送。在另一端,重新计算哈希值并进行比较,以确保在传输过程中没有对数据进行修改。 - Chesnokov Yuriy
@Chesnokov - 我添加了一个CRC32的链接,这是你应该使用的。 - Jonathan Dickinson

5

Jon Skeet有一个很好的答案,讲述如何重写GetHashCode,它基于通用的有效哈希技术,其中你从一个质数开始,将其加到组件的哈希码乘以另一个质数上,允许溢出。

对于您的情况,您可以这样做:

static int GetByteArrayHashCode(byte[] array)
{
    unchecked
    {
        int hash = 17;

        // Cycle through each element in the array.
        foreach (var value in array)
        {
            // Update the hash.
            hash = hash * 23 + value.GetHashCode();            
        }

        return hash;
    }
}

请注意Jon的回答中解释了为什么这比对各个元素的哈希值进行异或操作更好(而且在C#中,匿名类型目前不会对各个元素的哈希值进行异或操作,而是使用类似上述方法的东西)。
虽然这比System.Security.Cryptography命名空间中的哈希算法快(因为你处理的哈希值较小),但缺点是可能会有更多的冲突。
你需要针对你的数据进行测试,并确定在发生冲突时需要完成的工作与发生冲突的频率之间的平衡。

foreachfor 慢吗?此外,在 byte 上不需要调用 GetHashCode,因为它只返回其转换为 int 的值。 - Drew Noakes
@DrewNoakes 我很确定编译器会将数组上的 foreach 改为 for。但这只是一个实现细节,通常情况下,你应该测试一下是否存在瓶颈。同样,对于字节的 GetHashCode 返回值也是如此。 - casperOne

4
基于 编译器生成的 GetHashCode()
public static int GetHashCode(byte[] array) {
    unchecked {
        int i = 0;
        int hash = 17;
        int rounded = array.Length & ~3;

        hash = 31 * hash + array.Length;

        for (; i < rounded; i += 4) {
            hash = 31 * hash + BitConverter.ToInt32(array, i);
        }

        if (i < array.Length) {
            int val = array[i];
            i++;

            if (i < array.Length) {
                val |= array[i] << 8;
                i++;

                if (i < array.Length) {
                    val |= array[i] << 16;
                }
            }

            hash = 31 * hash + val;
        }

        return hash;
    }
}

啊...还有一个链接到C# Murmurhash http://landman-code.blogspot.com/2009/02/c-superfasthash-and-murmurhash2.html


不错的回答,但那是Murmur2,它在处理重复数据时存在问题(如果有的话,它会相当频繁地发生碰撞)。我不知道是否有任何C#版本的Murmur3。 - Jonathan Dickinson
Murmur3实现 http://blog.teamleadnet.com/2012/08/murmurhash3-ultra-fast-hash-algorithm.html - Carlos Blanco

2

任何加密哈希相关的内容都应该可用。关于速度并不确定,也许使用MD5?


在.NET中是否有任何自定义方法可用于快速比较byte[]数组,目前我不需要加密。 - Chesnokov Yuriy
@Chesnokov,这听起来像是一个不同的问题;就像:https://dev59.com/c3VD5IYBdhLWcg3wO5ED - Jonathan Dickinson
哦,不。我需要一种快速的方法来获取byte[]数组的32位值。序列化对象随其哈希一起发送到其他计算机,在那里重新计算哈希值并进行比较。 - Chesnokov Yuriy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接