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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18065251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:09:34  来源:igfitidea点击:

Concise way to combine field hashcodes?

c#javahash

提问by bacar

One if the ways to implement GetHashCode - where it's required to do so - is outlined by Jon Skeet here. Repeating his code:

Jon Skeet在此处概述了实现 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;
    }
}

Rolling this code by hand can be error-prone and bugs can be subtle/hard to spot (did you swap +and *by mistake?), it can be hard to remember the combination rules for different types, and I don't like expending mental effort on writing/reviewing the same thing over and over again for different fields and classes. It can also obfuscate one of the most important details (did I remember to include all the fields?) in repetitive noise.

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

Is there a concise way to combine field hashcodes using the .net library?. Obviously I could write my own, but if there's something idiomatic/built-in I'd prefer that.

是否有使用 .net 库组合字段哈希码的简洁方法?. 显然我可以自己写,但如果有一些惯用/内置的东西,我更喜欢那个。

As an example, in Java (using JDK7) I can achieve the above using:

例如,在 Java(使用 JDK7)中,我可以使用以下方法实现上述目的:

   @Override
   public int hashCode()  
   {  
      return Objects.hash(field1, field2, field3);  
   }  

This really helps to eliminate bugs and focus in the important details.

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

Motivation: I came across a C# class which requires an overridden GetHashCode(), but the way it combined the hashcodes of its various constituents had some severe bugs. A library function for combining the hashcodes would be useful for avoiding such bugs.

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

回答by bacar

Some people use:

有些人使用:

Tuple.Create(lastName, firstName, gender).GetHashCode()

It's mentioned on MSDN at Object.GetHashCode(), with the warning:

在 MSDN 上Object.GetHashCode()提到,并带有警告:

Note, though, that the performance overhead of instantiating a Tuple object may significantly impact the overall performance of an application that stores large numbers of objects in hash tables.

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

The logic of aggregating the constituent hashes is provided by System.Tuple, which hopefully has had some thought go into it...

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

Update: it is worth noting @Ryan's observation in the comments that this only appears to use the last 8 elements of any Tuple of Size>8.

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

回答by Jon Skeet

It's not exactly the same, but we have a HashCodeHelperclass in Noda Time(which has lots of types which override equality and hash code operations).

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

It's used like this (taken from ZonedDateTime):

它是这样使用的(取自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;
}

Note that it's a generic method, which avoids boxing for value types. It copes with null values automatically (using 0 for the value). Note that the MakeHashmethod has an uncheckedblock as Noda Time uses checked arithmetic as a project setting, whereas hash code calculations should be allowed to overflow.

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

回答by Pedro Abreu

public override GetHashCode()
{
    return this.Field1.GetHashCode() | this.Field2.GetHashCode | this.Field3.GetHashCode();
}

回答by Ryan

EDIT:Stay tuned, System.HashCode is coming to .NET Core, and will provide a singular best-practice way to create hashcodes. It will also be used under the hood by System.Tuple and other immutable composite types. Until it is released, the answer below is still useful.

编辑:敬请期待,System.HashCode 即将加入 .NET Core,并将提供一种独特的最佳实践方式来创建哈希码。System.Tuple 和其他不可变复合类型也将在后台使用它。在发布之前,下面的答案仍然有用。

For the sake of completeness, here is the hashing algorithm taken from the .NET Tuple Reference source, line 52. Interestingly, this hash algorithm was copied over from System.Web.Util.HashCodeCombiner.

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

Here is the code:

这是代码:

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));
}

Of course, the actual Tuple GetHashCode()(which is actually an Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer)) has a large switchblock to decide which one of these to call based upon how many items it is holding - your own code probably won't require that.

当然,实际的元组GetHashCode()(实际上是一个Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer))有一个大块switch来根据它持有的项目数量来决定调用其中的哪一个——您自己的代码可能不需要那样做。

回答by jbtule

Here are a couple of concise (though not as efficient) refactors of the System.Web.Util.HashCodeCombinermentioned in Ryan's answer

以下是System.Web.Util.HashCodeCombinerRyan回答中提到的一些简洁(虽然效率不高)的重构

    public static int CombineHashCodes(params object[] objects)
    {
        // From System.Web.Util.HashCodeCombiner
        int combine(int h1, int h2) => (((h1 << 5) + h1) ^ h2);

        return objects.Select(it => it.GetHashCode()).Aggregate(5381,combine);
    }

    public static int CombineHashCodes(IEqualityComparer comparer, params object[] objects)
    {
        // From System.Web.Util.HashCodeCombiner
        int combine(int h1, int h2) => (((h1 << 5) + h1) ^ h2);

        return objects.Select(comparer.GetHashCode).Aggregate(5381, combine);
    }