Using byte array as dictionary key
本问题已经有最佳答案,请猛点这里访问。
我想在
这很好,但是我确实意识到我的哈希代码生成器会产生很多重叠,在这些重叠中,事情最终会出现在同一个哈希桶中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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可以使事情变得更好。
这是非常关键的性能,所以也欢迎使用任何快捷方式。
[编辑]我最终使用了这个解决方案。我使用一个结构来包装字节数组,这样我就可以为它使用缓存的哈希代码,而不是为每个比较计算它。这导致了非常好的性能提升。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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; } } } |
哈希的更好选择可能是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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个或更多重复一次的数组,那么这可能不是最佳策略。
杂音杂音很快也很简单。有许多基于.NET的实现,但我不知道它们的性能如何。