Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/grails/5.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# 实现此复合GetHashCode()的最佳方法是什么_C#_.net_Computer Science_Hash - Fatal编程技术网

C# 实现此复合GetHashCode()的最佳方法是什么

C# 实现此复合GetHashCode()的最佳方法是什么,c#,.net,computer-science,hash,C#,.net,Computer Science,Hash,我有一个简单的课程: public class TileName { int Zoom, X, Y; public override bool Equals (object obj) { var o = obj as TileName; return (o != null) && (o.Zoom == Zoom) && (o.X == X) && (o.Y == Y); }

我有一个简单的课程:

public class TileName {
    int Zoom, X, Y;

    public override bool Equals (object obj)
    {
        var o = obj as TileName;
        return (o != null) && (o.Zoom == Zoom) && (o.X == X) && (o.Y == Y);
    }

    public override int GetHashCode ()
    {
        return (Zoom + X + Y).GetHashCode();
    }
}
我很好奇,如果我改为执行以下操作,是否会得到更好的散列码分布:

    public override int GetHashCode ()
    {
        return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();
    }

这个类将被用作字典键,所以我确实希望确保有一个合适的发行版。

我发现这个方法非常有效

public override int GetHashCode ()
{
    return (Zoom.ToString() + "-" + X.ToString() + "-" + Y.ToString()).GetHashCode();
}
public override int GetHashCode ()
{
    return Zoom.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();
}

您的问题中的两种实现都不是理想的。例如,它们将为
{Zoom=1,X=2,Y=3}
{Zoom=2,X=3,Y=1}
{Zoom=3,X=1,Y=2}
等返回完全相同的哈希值

我通常会这样使用:

public override int GetHashCode()
{
    // 269 and 47 are primes
    int hash = 269;
    hash = (hash * 47) + Zoom.GetHashCode();
    hash = (hash * 47) + X.GetHashCode();
    hash = (hash * 47) + Y.GetHashCode();
    return hash;
}

(从内存来看,我认为C#编译器在为匿名类型生成
GetHashCode
方法时使用了类似的方法。)

就像Jon Skeet所描述的那样,最好的做法是选择一些素数并将它们与单个哈希代码相乘,然后将所有内容相加

public int GetHashCode()
{
    unchecked
    {
        int hash = 17;
        // Maybe nullity checks, if these are objects not primitives!
        hash = hash * 23 + Zoom.GetHashCode();
        hash = hash * 23 + X.GetHashCode();
        hash = hash * 23 + Y.GetHashCode();
        return hash;
    }
}
xor
哈希的问题是:

  • 如果
    X
    等于
    Y
    ,则散列将只是缩放,因为
    X^Y=X^X=0
    会保持不变
  • xor
    是一个对称运算符,它将为对象生成完全相同的哈希值
    [Zoom=3,X=5,Y=7]
    [Zoom=3,X=7,Y=5]
    [Zoom=7,X=5,Y=3]
这些事实使得xor方法更有可能导致冲突

除了JONPOST之外,考虑使用<代码>未选中的上下文,以明确忽略溢出。因为就像上面说的:

如果未选中
未选中
,则 使用时,常量表达式使用 编译时的默认溢出检查 时间,这是检查。否则,如果 表达式是非常量的 运行时溢出检查取决于 其他因素,如编译器选项 和环境配置

因此,虽然通常会取消检查溢出,但可能是在某些环境中或使用某些编译器选项构建时失败。但在这种情况下,您不希望显式地检查这些溢出

更新:

顺便说一下:
someInt.GetHashCode()
返回
someInt
。像这样,它当然是最快的,并且是一个完美的散列分布,没有任何冲突。否则您将如何将int映射到int散列?:)我想说的是:你的第一个方法:

return (Zoom + X + Y).GetHashCode();
还有你的第二个:

return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();
它们完全一样。您甚至不必调用
GetHashCode
,两者都很可能发生冲突。如果三个整数的整数值都很小,那么可能比xor方法更糟糕

更新2:

正如我在Chaospantions post的评论中所写:如果你只有这三个int值,
X
Y
Zoom
都是相对较小的数字(小于1000或10000),那么这一个也可能是一个很好的散列生成器:

public int GetHashCode()
{
    return (X << 16) ^ (Y << 8) ^ Zoom;
}
public int GetHashCode()
{

return(X我知道这个问题有点老了,但是现在你可以通过System.HashCode类轻松地创建你的哈希代码

在这种情况下,它看起来像

public override int GetHashCode()
{
    return HashCode.Combine(Zoom, X, Y);
}


这可能会提供一个很好的分布,但对性能来说确实很糟糕,因为每次调用GetHashCode时至少会创建一个新字符串和一个新字符串数组。您的分布比这更糟糕。@Steven,这可以在计算后缓存,并在设置Zoom、X或Y时清除缓存的值。@Fede:您可以缓存慢速算法的结果,或者只使用快速算法。顺便说一句:只有当您有只读字段,或者您必须存储字段的旧值时,缓存才有意义。这会变得混乱…@Philip:您不需要存储旧值。您可以将GetHashCode的结果缓存在可为null的int中。如果缓存为null,则计算它,如果它为isn然后不返回该值。当设置影响缓存的字段时,只需将缓存设置为null。当操作成本乘以调用次数是瓶颈的原因时,缓存是有意义的。OP确实要求进行适当的分发。性能可能是一个问题,但我们关注的数据集大小是多少?虽然这是一个问题与问题中的实现相比,它仍然不是很好。例如,它没有考虑字段顺序,因此
{Zoom=1,X=2,Y=3}
{Zoom=2,X=3,Y=1}
{Zoom=3,X=1,Y=2}
etc等都会返回相同的散列。某种滚动乘法和/或求和可以避免这种情况(并且可能会提供更好的分布).@Luke:同意。@ChoasPandion:请在这里阅读:@Luke-我同意,通常我会尝试使用最简单的解决方案来解决任何问题。对于任何严肃的应用程序,你都会希望使用碰撞概率较小的算法。@ChaosPandion:Jon Skeets的解决方案同样简单,碰撞概率较小。不是这样的一个非常复杂的大型算法。如果你不关心碰撞,你可以对每个实例静态地返回1;
。好吧,开玩笑吧…:DJust想出了一个新主意,也许是我们两个解决方案之间的折衷:如果你只需要这三个int值,并且
X
Y
Zoom
是相对的非常小的数字(小于1000或10000)这一个可能也是一个很好的哈希生成器:
return(X@Philip:我以前见过Jon提到过它,但我不记得我最初从哪里捡到的。我认为这是一个相当常见的实现。是的,这只是一个好的实践,更多的人应该习惯。小警告:请确保在创建实例的散列代码不能被更改,否则就无法在散列中找到键(我想是FxCop valida)
public override int GetHashCode()
{
    return HashCode.Combine(Zoom, X, Y);
}