5

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

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()。谁能解释为什么 t1 和 t2 对象的哈希码不同?

4

4 回答 4

20

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

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

因此,特别是,您的两个实例t1实际上t2并不相等,因为您没有覆盖Object.equals. 它们不等于引用,因此不等于 per 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方法必须产生相同的整数结果。

Let's see if we have satisfied this requirement here. If x and y are instances of Test and satisfy x.equals(y) is true, we have that x.i == y.i and x.j == y.j. Then, clearly, if we invoke x.hashCode() and y.hashCode() we have the invariant that at each line of execution in Test.hashCode we will have hash holding the same value. Clearly this is true on the first line since hash will be 17 in both cases. It will hold on the second line since this.i will return the same value whether this == x or this == y because x.i equals y.i. Finally, on the penultimate line, we will still have hash being equal across both invocations because x.j equals y.j is true as well.

Note that there is one last piece of the contract that we haven't discussed yet. This is the requirement that hashCode return a consistent value during a single execution of a Java application:

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.

这样做的必要性是显而易见的。如果您hashCode在同一应用程序的单次执行期间更改返回值,您可能会在hashCode用于跟踪对象的类似哈希表的数据结构中丢失对象。尤其是,这就是为什么在类似哈希表的数据结构中改变作为键的对象是纯粹的邪恶。不要这样做。我什至会争辩说它们应该是不可变的对象。

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

他们都覆盖了Object.equalsObject.hashCode

于 2013-06-18T03:02:33.680 回答
3

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

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

是两个不同的对象,如果你不覆盖equals这里的方法,当且仅当你这样做时它们是相等的

Test t2 = t1;

当您在这里创建两个不同的对象时,不相等的哈希码因为它们不引用相同objecthashcodes所以必须不同记住

  • 如果两个对象相等,则它们的哈希码MUST相等
  • 但是如果哈希码相等,那么对象就不必相等
于 2013-06-18T03:07:11.877 回答
1

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

于 2013-06-18T03:33:25.087 回答
1

这是因为在 Java中equals的默认实现。hashCode

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

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

于 2013-06-18T03:03:17.917 回答