将对象的哈希码定义为所有类变量哈希码的和,乘,等等是不正确的吗?

dev*_*ium 9 .net c# java hashcode

假设我有以下课程:

class ABC {
    private int myInt = 1;
    private double myDouble = 2;
    private String myString = "123";
    private SomeRandomClass1 myRandomClass1 = new ...
    private SomeRandomClass2 myRandomClass2 = new ...

    //pseudo code
    public int myHashCode() {
        return 37 *
               myInt.hashcode() *
               myDouble.hashCode() *
               ... *
               myRandomClass.hashcode()
    }
}
Run Code Online (Sandbox Code Playgroud)

这是hashCode的正确实现吗?这不是我通常这样做的方式(我倾向于遵循Effective Java的指导方针),但我总是有诱惑去做类似上面代码的事情.

谢谢

pol*_*nts 13

这取决于你所说的"正确".假设您正在使用hashCode()所有相关的equals()定义字段,那么是的,它是"正确的".然而,这样的公式可能不会有良好的分布,因此可能会导致更多的碰撞,这将对性能产生不利影响.

以下是Effective Java 2nd Edition的引用,第9项:覆盖hashCode时始终覆盖equals

虽然这个项目中的配方产生了相当好的散列函数,但它不会产生最先进的散列函数,Java平台库也不提供1.6版本的散列函数.编写这样的哈希函数是一个研究课题,最好留给数学家和计算机科学家.[...尽管如此,]此项目中描述的技术应该适用于大多数应用程序.

它可能不需要很多数学能力来评估你提出的哈希函数有多好,但为什么还要费心呢?为什么不遵循经过传闻证明在实践中足够的东西呢?

Josh Bloch的食谱

  • 在一个int名为的变量中存储一些常量非零值,比如17 result.
  • 计算每个字段的int哈希码c:
    • 如果该字段是a boolean,则计算(f ? 1 : 0)
    • 如果该字段是a byte, char, short, int,则计算(int) f
    • 如果该字段是a long,则计算(int) (f ^ (f >>> 32))
    • 如果该字段是a float,则计算Float.floatToIntBits(f)
    • 如果该字段是a double,则计算Double.doubleToLongBits(f),然后long如上所述对结果进行散列.
    • 如果该字段是一个对象引用,并且该类的equals方法通过递归调用来比较该字段equals,则hashCode在该字段上递归调用.如果该字段的值为null,则返回0.
    • 如果该字段是数组,则将其视为每个元素都是单独的字段.如果数组字段中的每个元素都很重要,则可以使用Arrays.hashCode版本1.5中添加的方法之一.
  • 将哈希码组合cresult如下:result = 31 * result + c;

现在,当然,这个配方相当复杂,但幸运的是,你不必每次都重新实现它,这要归功于java.util.Arrays.hashCode(Object[])(并com.google.common.base.Objects提供了一个方便的变量).

@Override public int hashCode() {
    return Arrays.hashCode(new Object[] {
           myInt,    //auto-boxed
           myDouble, //auto-boxed
           myRandomClass,
    });
}
Run Code Online (Sandbox Code Playgroud)

也可以看看

  • Object.hashCode()

    如果两个对象根据方法不相等则不是必需的,则在两个对象中的每一个上equals(java.lang.Object)调用hashCode方法必须产生不同的整数结果.但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能.