结合字段哈希码的简洁方法?

bac*_*car 24 c# java hash

如果一个实现GetHashCode的方式-在那里它必须这样做-由乔恩斯基特概述这里.重复他的代码:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}
Run Code Online (Sandbox Code Playgroud)

用手滚动这个代码可以是容易出错,错误可能是微妙/很难发现(你交换+*误?),它可能很难记住不同类型的组合规则,我不喜欢花费精神努力为不同的领域和课程一遍又一遍地写同样的东西.它还可以模糊重复噪声中最重要的细节之一(我记得包括所有字段吗?).

是否有使用.net库组合字段哈希码的简明方法?.显然我可以写自己的,但如果有一些惯用/内置的东西,我宁愿这样做.

举个例子,在Java中(使用JDK7),我可以使用以下方法实现:

   @Override
   public int hashCode()  
   {  
      return Objects.hash(field1, field2, field3);  
   }  
Run Code Online (Sandbox Code Playgroud)

这确实有助于消除错误并专注于重要细节.

动机:我遇到了一个需要覆盖的C#类GetHashCode(),但它结合各种成分的哈希码的方式有一些严重的错误.用于组合哈希码的库函数对于避免这样的错误是有用的.

bac*_*car 17

有些人使用:

Tuple.Create(lastName, firstName, gender).GetHashCode()
Run Code Online (Sandbox Code Playgroud)

在MSDN上Object.GetHashCode()提到它,警告:

但请注意,实例化Tuple对象的性能开销可能会显着影响在哈希表中存储大量对象的应用程序的整体性能.

聚合成分散列的逻辑是由提供的System.Tuple,希望有一些想法进入它...

更新:值得注意的是@ Ryan在评论中的观察结果,这似乎只使用了任何大小> 8的元组的最后8个元素.

  • @Servy 你会这么认为吧?我确实遇到了一个有问题的实现,因此是动机。有很多错误你可能会不小心放在这里 - 交换加法/乘法,乘法器选择不当,完全忘记加法部分......我见过它们发生,最糟糕的是它们_有点看起来_类似于'标准”解决方案,最终也通过了代码审查。我相信你应该在解决瓶颈的地方推出自己的产品,但在没有解决瓶颈的地方尽量减少维护人员的认知负担。 (2认同)

Rya*_*yan 12

编辑:请继续关注,System.HashCode即将推出.NET Core,并将提供一种创建哈希码的独特最佳实践方法.它也将在System.Tuple和其他不可变复合类型的引擎下使用.在它发布之前,下面的答案仍然有用.

为了完整起见,这里是从.NET元组参考源第52行中获取的散列算法System.Web.Util.HashCodeCombiner.有趣的是,这个散列算法被复制了.

这是代码:

public override int GetHashCode() {
    // hashing method taken from .NET Tuple reference
    // expand this out to however many items you need to hash
    return CombineHashCodes(this.item1.GetHashCode(), this.item2.GetHashCode(), this.item3.GetHashCode());
}

internal static int CombineHashCodes(int h1, int h2) {
    // this is where the magic happens
    return (((h1 << 5) + h1) ^ h2);
}

internal static int CombineHashCodes(int h1, int h2, int h3) {
    return CombineHashCodes(CombineHashCodes(h1, h2), h3);
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4) {
    return CombineHashCodes(CombineHashCodes(h1, h2), CombineHashCodes(h3, h4));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), h5);
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7));
}

internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) {
    return CombineHashCodes(CombineHashCodes(h1, h2, h3, h4), CombineHashCodes(h5, h6, h7, h8));
}
Run Code Online (Sandbox Code Playgroud)

当然,实际的Tuple GetHashCode()(实际上是一个Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer))有一个很大的switch块来决定根据它持有多少项来决定调用哪一个 - 你自己的代码可能不会要求它.


Jon*_*eet 8

它并不完全相同,但我们HashCodeHelperNoda Time中有一个类(它有很多类型可以覆盖相等和哈希代码操作).

它是这样使用的(取自ZonedDateTime):

public override int GetHashCode()
{
    int hash = HashCodeHelper.Initialize();
    hash = HashCodeHelper.Hash(hash, LocalInstant);
    hash = HashCodeHelper.Hash(hash, Offset);
    hash = HashCodeHelper.Hash(hash, Zone);
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

请注意,它是一种通用方法,可以避免对值类型进行装箱.它自动处理空值(使用0作为值).请注意,该MakeHash方法有一个unchecked块,因为Noda Time使用checked算法作为项目设置,而哈希码计算应该允许溢出.

  • @HighCore:看似特殊情况可能是其他人的日常工作.不是每个人都写相同类型的代码. (7认同)