Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#哈希代码生成器_C#_Java - Fatal编程技术网

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() *

我过去经常使用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() * (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
    数组的分配和一个循环——如果您只需要处理两个成员,那么这会带来相当多的不必要的开销
这两个缺点都不会导致任何错误,而仅仅是效率低下;并且,这两种方法都会在探查器中显示为此方法或散列代码使用者内部的光点。

C#没有内置的散列代码生成器,但您可以自己运行。我最近遇到了这个精确的问题,通过使用泛型创建了这个不使用装箱的hashcode生成器,并实现了一个用于生成特定hash的修改算法。但是你可以使用任何你喜欢的算法,比如
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);
}