hashcode的默认实现为以相同方式构造的对象返回不同的值

时间:2013-06-18 02:58:53

标签: java

我在这里写一个示例代码:

public class Test {

    private int i;
    private int j;

    public Test() {
        // TODO Auto-generated constructor stub
    }

    public Test(int i, int j)
    {
        this.i=i;
        this.j=j;
    }
}

现在我正在创建两个对象:

Test t1= new Test(4,5);
Test t2 = new Test(4,5);

但是当我打印t1.hashcode()和t2.hashcode()时,它们会给出不同的值。 但是根据java的一般联系,他们应该返回相同的值。 事实上,当我使用String或Integer做同样的事情时,他们返回相同的hashcode()。任何人都可以解释为什么hashcode对于t1和t2对象不同?

4 个答案:

答案 0 :(得分:19)

  

但是根据java的一般联系,他们应该返回相同的值。

Java的equals-hashCode合同要求如果两个对象相等Object.equals,则它们必须具有Object.hashCode中相同的哈希码。但Object.equals默认实现是引用相等,因此当且仅当它们是同一个实例时,两个实例是相同的。

因此,特别是,您的两个实例t1t2实际上相等,因为您没有覆盖Object.equals。它们不等于引用,因此不等于Object.equals,因此hashCode可能返回不同的值。事实上,合同明确说明如下:

  

如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。

因此,我们在此处没有违反equals-hashCode合同。

因此,对于您的对象,如果您希望不同的实例在相等的逻辑定义中相等,则需要覆盖Object.equals

 @Override
 public boolean equals(Object obj) {
     if (obj == null) {
         return false;
     if (this == obj) {
          return true;
     }
     if (!(obj instanceof Test)) {
          return false;
     }
     Test other = (Test)obj; 
     return this.i == other.i && this.j == other.j;
 }

equals-hashCode合同要求您覆盖Object.hashCode,否则您将遇到一些令人讨厌的错误:

 @Override
 public int hashCode() {
     int hash = 17; 
     hash = 31 * hash + this.i;
     hash = 31 * hash + this.j;
     return hash;
 }

合同说什么:

  

如果两个对象根据equals(Object)方法相等,则在两个对象中的每一个上调用hashCode方法必须产生相同的整数结果。

让我们看看我们是否满足了这个要求。如果xyTest的实例并且x.equals(y)满足true,则我们会x.i == y.ix.j == y.j。然后,显然,如果我们调用x.hashCode()y.hashCode(),我们就会得到不变量,即在Test.hashCode的每一行执行中,我们将hash保持相同的值。显然,这是第一行,因为hash在两种情况下都是17。它将保留在第二行,因为this.i将返回相同的值this == xthis == y,因为x.i等于y.i。最后,在倒数第二行,我们仍然会在两次调用中hash相等,因为x.j等于y.j也是如此。

请注意,我们还没有讨论合同的最后一部分。这是hashCode在单次执行Java应用程序期间返回一致值的要求:

  

每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象上的equals比较中使用的信息。

这一点的必要性是显而易见的。如果在单次执行同一应用程序期间从hashCode更改返回值,则可能会丢失使用hashCode来跟踪对象的类哈希表数据结构中的对象。特别是,这就是为什么变异对象是哈希表类数据结构中的键是纯粹的邪恶;不要这样做。我甚至认为它们应该是不可改变的对象。

  

事实上,当我与StringInteger做同样的事情时,他们会返回相同的hashcode()

他们都覆盖了Object.equalsObject.hashCode

答案 1 :(得分:3)

您没有覆盖类中的equals方法,因此将使用属于Object类的默认方法。 对象类方法只是检查引用是否它们是指同一个对象。

Test t1 = new Test(4,5);
Test t2 = new Test(4,5);

是两个不同的对象,如果你不在这里覆盖equals方法,当且仅当你做

时它们将是相等的
Test t2 = t1;

当你在这里创建两个不同的对象时,hashcode不相等,因为它们不引用相同的objecthashcodes必须是不同的 记住

  • 如果两个对象相等,则其哈希码MUST相等
  • 但是如果哈希码相等,那么对象应该是相等的

答案 2 :(得分:1)

这是因为Java中的equalshashCode的默认实现。

JVM无法知道您如何确定两个对象是否相同。它的作用是使用内存引用。因此,默认情况下,equalshashCode方法会比较内存引用;即两个不同的对象从不 .equals

如果您想覆盖此行为(如果您希望使用Collection,建议您这样做),那么您需要做的就是实现自己的equals和{{ 1}}方法。

答案 3 :(得分:1)

问题是t1和t2不是同一个对象,它们是不同的对象。使用new创建的所有对象都是不同的对象。默认的hashCode()实现通常会为不同的对象返回不同的哈希码。请参阅Object.hashCode API。