Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

default implementation of hashcode returns different values for objects constructed the same way

Tags:

java

Here I am writing one sample code:

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

now I am creating two objects as bellow:

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

But when i am printing t1.hashcode() and t2.hashcode() they are giving different values. But as per java's general contact they should return same value. In fact, when i am doing same thing with String or Integer they are returning same hashcode(). Can anyone please explain why hashcode is different for t1 and t2 object?

like image 605
user2392631 Avatar asked Jun 18 '13 02:06

user2392631


2 Answers

But as per java's general contact they should return same value.

Java's equals-hashCode contract requires that if two objects are equal by Object.equals, they must have the same hashcode from Object.hashCode. But the default implementation of Object.equals is reference equality, and therefore two instances are the same if and only if they are the same instance.

Therefore, in particular, your two instances t1 and t2 are in fact not equal because you have not overridden Object.equals. They are not equal as references, and therefore not equal per Object.equals, and therefore it is acceptable for hashCode to possibly return different values. In fact, the contract explicitly says the following:

It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.

Thus, we do not have a violation of the equals-hashCode contract here.

So, for your objects, if you want different instances to be equal per a logical definition of equality, you need to override 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;
 }

And the equals-hashCode contract requires that you override Object.hashCode too or you'll run into some nasty bugs:

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

What does the contract say:

If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

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.

The necessity of this is obvious. If you change the return value from hashCode during a single execution of the same application, you could lose your objects in hashtable-like data structures that use hashCode to keep track of objects. In particular, this is why mutating objects that are keys in hashtable-like data structures is pure evil; don't do it. I would go so far as to argue that they should be immutable objects.

In fact, when i am doing same thing with String or Integer they are returning same hashcode().

They've both overridden Object.equals and Object.hashCode.

like image 83
jason Avatar answered Nov 08 '22 16:11

jason


You have not overridden the equals method in your class so the default one will be used that belongs to Object class. Object class methods simply checks for the references whether they are referring to the same object or not.

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

are two different objects, if you don't override the equals method here, they will be equal if and only if you do

Test t2 = t1;

As you are creating two different objects here, hashcode which are NOT equal because they don't refer to the same object, hashcodes must be differnt Remember

  • If two objects are equal, then their hashcode MUST be equal
  • But if hashcodes are equal, then its not necessary that objects should be equal
like image 45
Prasad Kharkar Avatar answered Nov 08 '22 14:11

Prasad Kharkar