【发布时间】:2010-05-26 11:13:26
【问题描述】:
我以前经常使用 apache hashcode builder
这对于 C# 是否存在
【问题讨论】:
-
这里是 Murmur 和 XXHash 的 C# 实现。 geteventstore.com/blog/?p=36
我以前经常使用 apache hashcode builder
这对于 C# 是否存在
【问题讨论】:
这是我自制的建造者。
用法:
hash = new HashCodeBuilder().
Add(a).
Add(b).
Add(c).
Add(d).
GetHashCode();
a,b,c 和 d 是什么类型的字段无关紧要,易于扩展,无需创建数组。
来源:
public sealed class HashCodeBuilder
{
private int hash = 17;
public HashCodeBuilder Add(int value)
{
unchecked
{
hash = hash * 31 + value; //see Effective Java for reasoning
// can be any prime but hash * 31 can be opimised by VM to hash << 5 - hash
}
return this;
}
public HashCodeBuilder Add(object value)
{
return Add(value != null ? value.GetHashCode() : 0);
}
public HashCodeBuilder Add(float value)
{
return Add(value.GetHashCode());
}
public HashCodeBuilder Add(double value)
{
return Add(value.GetHashCode());
}
public override int GetHashCode()
{
return hash;
}
}
示例用法:
public sealed class Point
{
private readonly int _x;
private readonly int _y;
private readonly int _hash;
public Point(int x, int y)
{
_x = x;
_y = y;
_hash = new HashCodeBuilder().
Add(_x).
Add(_y).
GetHashCode();
}
public int X
{
get { return _x; }
}
public int Y
{
get { return _y; }
}
public override bool Equals(object obj)
{
return Equals(obj as Point);
}
public bool Equals(Point other)
{
if (other == null) return false;
return (other._x == _x) && (other._y == _y);
}
public override int GetHashCode()
{
return _hash;
}
}
【讨论】:
hash 中,这比每次调用 GetHashCode 时进行正常计算更有效。
我使用以下:
public static int ComputeHashFrom(params object[] obj) {
ulong res = 0;
for(uint i=0;i<obj.Length;i++) {
object val = obj[i];
res += val == null ? i : (ulong)val.GetHashCode() * (1 + 2 * i);
}
return (int)(uint)(res ^ (res >> 32));
}
使用这样的助手快速、简单且可靠,但它有两个潜在的缺点(您不太可能经常遇到,但需要注意):
int x、ComputeHashFrom(x*-3, x) == 0 - 因此,如果您的对象具有某些病态属性,您可能会遇到许多哈希码冲突,从而导致字典和哈希集性能不佳。这不太可能发生,但类型感知的哈希码计算可以更轻松地避免此类问题。params 数组和一个循环 - 如果您只有两个成员要处理,这会产生相当多的不必要的开销。这些缺点都不会导致任何错误,只是效率低下;并且两者都在分析器中显示为此方法或哈希码使用者内部的光点。
【讨论】:
.GetHashCode 便宜得多,因此如果遇到几个 冲突,您最终只需支付很少的费用。另一方面,如果您正在进行大量的集合/字典计算,您可以简单地缓存未更改对象的哈希码;但是您无法避免哈希码错误的后果。无论如何,在实践中我不会为任何更复杂的事情而烦恼,直到分析表明它是值得的——它几乎从来没有这样做过。
C# 没有内置的 HashCode 构建器,但您可以自己构建。我最近遇到了这个精确的问题,并通过使用泛型创建了这个不使用装箱的哈希码生成器,并实现了修改后的FNV 算法来生成特定的哈希。但是您可以使用任何您喜欢的算法,例如 System.Security.Cryptography 中的算法之一。
public static int GetHashCode<T>(params T[] args)
{
return args.GetArrayHashCode();
}
public static int GetArrayHashCode<T>(this T[] objects)
{
int[] data = new int[objects.Length];
for (int i = 0; i < objects.Length; i++)
{
T obj = objects[i];
data[i] = obj == null ? 1 : obj.GetHashCode();
}
return GetFnvHash(data);
}
private static int GetFnvHash(int[] data)
{
unchecked
{
const int p = 16777619;
long hash = 2166136261;
for (int i = 0; i < data.Length; i++)
{
hash = (hash ^ data[i]) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return (int)hash;
}
}
【讨论】:
Microsoft 最近发布了一个计算哈希码的类。请参阅https://docs.microsoft.com/en-us/dotnet/api/system.hashcode。您需要在项目中包含 NuGet 包 Microsoft.Bcl.HashCode 才能使用它。
使用示例:
using System.Collections.Generic;
public class MyClass {
public int MyVar { get; }
public string AnotherVar { get; }
public object MoreVars;
public override int GetHashCode()
=> HashCode.Combine(MyVar, AnotherVar, MoreVars);
}
【讨论】:
现在我利用 ValueTuples、ref Tuples 或匿名类型:
var hash = (1, "seven").GetHashCode();
var hash2 = Tuple.Create(1, "seven").GetHashCode();
var hash3 = new { Number = 1, String = "seven" }.GetHashCode();
我相信值元组会最快。
【讨论】: