使用字节数组作为字典键[重复]

Posted

技术标签:

【中文标题】使用字节数组作为字典键[重复]【英文标题】:Using byte array as dictionary key [duplicate] 【发布时间】:2016-01-07 00:44:05 【问题描述】:

我想使用字节数组作为concurentDictionary 中的查找键。 目前我通过使用自定义 EqualityComparer<byte[]> 来解决这个问题。

这很好用,但我确实意识到我的哈希码生成器会产生很多重叠,最终会出现在同一个哈希桶中。

public class ByteArrayEqualityComparer : EqualityComparer<byte[]>

    public override bool Equals(byte[] x, byte[] y)
    
        //fast buffer compare
        return UnsafeCompare(x, y);
    

    public override int GetHashCode(byte[] obj)
    
        int hash = 0;
        for (int i = 0; i < obj.Length; i += 2)
        
            hash += obj[i]; //xor? shift? black magic?
        
        return hash;
    

什么是从字节数组创建相对快速的哈希的好公式?

我的想法是,我可以通过跳过每 x 个字节来计算哈希码以提高速度。 由于最终的比较仍然是在整个数据集上完成的,所以多次比较所有字节似乎没有意义。

我想一些 xor 魔法和对 hash var 的转移会让事情变得更好。

这对性能至关重要,因此也欢迎任何可以使用的快捷方式。

[编辑] 我最终使用了这个解决方案。 我使用一个结构来包装字节数组,这样我就可以为它使用缓存的哈希码,而不是为每次比较计算它。 这带来了非常好的性能提升。

public struct ByteArrayKey

    public readonly byte[] Bytes;
    private readonly int _hashCode;

    public override bool Equals(object obj)
    
        var other = (ByteArrayKey) obj;
        return Compare(Bytes, other.Bytes);
    

    public override int GetHashCode()
    
        return _hashCode;
    

    private static int GetHashCode([NotNull] byte[] bytes)
    
        unchecked
        
            var hash = 17;
            for (var i = 0; i < bytes.Length; i++)
            
                hash = hash*23 + bytes[i];
            
            return hash;
        
    

    public ByteArrayKey(byte[] bytes)
    
        Bytes = bytes;
        _hashCode = GetHashCode(bytes);
    

    public static ByteArrayKey Create(byte[] bytes)
    
        return new ByteArrayKey(bytes);
    

    public static unsafe bool Compare(byte[] a1, byte[] a2)
    
        if (a1 == null || a2 == null || a1.Length != a2.Length)
            return false;
        fixed (byte* p1 = a1, p2 = a2)
        
            byte* x1 = p1, x2 = p2;
            var l = a1.Length;
            for (var i = 0; i < l/8; i++, x1 += 8, x2 += 8)
                if (*(long*) x1 != *(long*) x2) return false;
            if ((l & 4) != 0)
            
                if (*(int*) x1 != *(int*) x2) return false;
                x1 += 4;
                x2 += 4;
            
            if ((l & 2) != 0)
            
                if (*(short*) x1 != *(short*) x2) return false;
                x1 += 2;
                x2 += 2;
            
            if ((l & 1) != 0) if (*x1 != *x2) return false;
            return true;
        
    

【问题讨论】:

标准模式是例如here。如果这对您有用,我将关闭此问题作为重复。 这正是我所需要的,谢谢! 我真的不明白这是怎么重复的,因为问题的核心是字节数组的比较 【参考方案1】:

哈希的更好选择可能是这样的:

public override int GetHashCode(byte[] obj)

    int hash = 0;
    for (int i = 0; i < obj.Length; i++)
    
        exponents = [0, 8, 16, 24];
        exponent = exponents[i % 4];

        unchecked
        
            hash += obj[i] * (1 << i);
        
    
    return hash;

从概念上讲,这会将每个 4 字节的块转换为一个 int,因为两者都是 32 位,然后将它们与标准整数溢出相加。因此,所有长度为 4 或更少的唯一字节数组将映射到不同的哈希码,并且(给定随机数据)较大的数组应该很好地分布在哈希空间中。如果您期望有很多非常相似的数组,或者每 4 次重复的数组,这可能不是最佳策略。

【讨论】:

【参考方案2】:

MurmurHash 非常快而且非常简单。有许多基于 .NET 的实现,但我不知道它们的性能如何。

【讨论】:

以上是关于使用字节数组作为字典键[重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用字节数组作为 Map 键

如何使用int数组作为字典键C# [重复]

在两个字典数组中查找重复键以使用新数组字典更新旧数组字典

如何在golang中将字节附加到字节数组?不是字节数组到字节数组切片等[重复]

Java将字节数组转换为十六进制字节数组[重复]

如何在c ++中将int数组转换为字节数组[重复]