C#哈希代码生成器
我过去经常使用ApacheHashCodeBuilder 这是否适用于C#我使用以下方法:C#哈希代码生成器,c#,java,C#,Java,我过去经常使用ApacheHashCodeBuilder 这是否适用于C#我使用以下方法: 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() *
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));
}
public static int ComputeHashFrom(params object[]obj){
ulongres=0;
对于(uint i=0;i>32));
}
使用这样一个助手是快速、简单和可靠的,但它有两个潜在的缺点(你不太可能经常遇到,但要知道这一点很好):
- 它可以为一些参数的分布生成糟糕的哈希代码。例如,对于任何
,int x
-因此,如果对象具有某些病理属性,则可能会发生许多哈希代码冲突,从而导致性能较差的字典和哈希集。这种情况不太可能发生,但类型感知哈希代码计算可以更容易地避免此类问题ComputeHashFrom(x*-3,x)==0
- 哈希代码的计算比专门的计算要慢。特别是,它涉及到
数组的分配和一个循环——如果您只需要处理两个成员,那么这会带来相当多的不必要的开销params
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;
}
}
public static int GetHashCode(参数T[]args)
{
返回args.GetArrayHashCode();
}
公共静态int GetArrayHashCode(此T[]对象)
{
int[]data=newint[objects.Length];
for(int i=0;i7;
hash+=hash>17;
这是我自制的生成器
用法:
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;
}
}
现在我利用ValueTuple、ref Tuple或匿名类型:
var hash = (1, "seven").GetHashCode();
var hash2 = Tuple.Create(1, "seven").GetHashCode();
var hash3 = new { Number = 1, String = "seven" }.GetHashCode();
我相信值元组将是最快的。Microsoft最近发布了一个用于计算哈希代码的类。请参阅。您需要在项目中包含NuGet包才能使用它
用法示例:
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);
}
关于速度,我要补充一点,您也可以使用类型感知方法生成更好的哈希代码,特别是如果您对最常发生的值有合理的了解。我倾向于更关心这一点,而不是计算速度。这种简单安全的方法的要点是简单安全。对于某些发行版对于对象,这将导致较差的hashcode,并且总是需要更多的时间来计算。但是,在一般情况下,这些hashcode工作正常(毕竟,组成obj的成员通常都有类型感知的GetHashCode实现),并且大多数程序不会花费大部分时间制作或使用哈希代码。然而,从实践经验来看,我会注意到,我遇到了GetHashCode性能问题,但在这个实现-YMMV中没有遇到质量问题。在实践中,由于对象相等性比较通常比便宜得多。GetHashCode
,因此您最终会发现p如果您遇到更多冲突,只需支付很少的费用。另一方面,如果您正在进行大量的集合/字典计算,您可以简单地缓存未更改对象的哈希代码;但您无法避免错误哈希代码的后果。无论如何,在实际操作中,我不会为任何更复杂的事情操心,直到分析显示它是有效的是的,我同意。正如我所说的,我想在你关于速度的警告中加上这一点,但这并不意味着我不认为这是一个合理的通用方法。我喜欢封装这种可重用代码的概念。但是,方法调用是否会导致性能损失?@SteveB取决于你如何使用它。考虑到这一点,你应该考虑一下我只在哈希代码中使用不可变数据,如果你在构造函数中这样做一次,然后将结果存储到私有成员hash
,这比每次调用GetHashCode
时进行正常计算更有效。@SteveB添加了示例用法来说明我的意思。也有正确的equals实现。C#Mu的实现rmur和XXHash在这里。
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);
}