3

While reading about equals() and hashcode(), I came to know that if two objects are equal, then their hashcodes must be equal, though not vice-versa.

But below example doesn't reflect this.

class Employee{

  private String name;

  Employee(String name){
    this.name = name;
  }

  @Override
  public boolean equals(Object obj) {           
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

}

Now if I create two Employee objects as

Employee e1 = new Employee("hi");
Employee e2 = new Employee("hi");

If i do, e1.equals(e2), it returns true even though their hashcodes are different which is evident from printing, e1.hashcode() and e2.hashcode().

Can someone explain me?

Anand
  • 20,708
  • 48
  • 131
  • 198
  • 6
    The contract on `equals` and `hashCode` must be maintained _by the programmer_; you have not overriden `hashCode` so that it behaves in the desired way. It defaults to the implementation in `Object` which results in the identity hash code. – obataku Oct 13 '12 at 18:11
  • Just because, your are checking incorrect implementation – Elbek Oct 13 '12 at 18:36

4 Answers4

11

You need to override hashcode method and provide implementation which is in contract with equals.

   @Override
    public int hashCode() {
        return name == null ? 0 : name.hashCode();
    }
  • if a class overrides equals, it must override hashCode
  • when they are both overridden, equals and hashCode must use the same set of fields
  • if two objects are equal, then their hashCode values must be equal as well
  • if the object is immutable, then hashCode is a candidate for caching and lazy initialization

You can read about implementing hashcode here

If you don't override the method default behavior will be used from Object class.

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)

Hash based [HashMap,HashSet,Hashtable,LinkedHashSet,WeakHashMap] collections will use hashCode() to find/store objects in buckets and then they will call equals().

Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
  • hi, do you know whether ArrayList.contains follow the contract? Will it check the hash before it check the equal? – Sam YC Nov 02 '12 at 14:38
  • @GMsoF ArrayList contains method does not make use of hashcode(). it only checks equality based on equals(). – Amit Deshpande Nov 02 '12 at 15:28
3

This is because, everytime you override equals method you must also override hashcode method.

Else, your objects will be compared according to your code, but their HashCode will be calculated according to the pre-defined algorithm in the Object class.

NOTE: - In general, what all parameters you have considered in checking whether your objects are equals or not, you should use all those parameters to calculate the hashcodes for each object.

See this very good post which describes the use of equals and hashcode method.

To quote a line from this post, that I already described above: -

Use same set of fields to compute hashcode that you used in equals method

Let's see the below Demo to understand the above statement: -

public class Demo {
    private String str;
    private String name;

    public boolean equals(Object obj) {
        // Suppose you compare two objects based on length of their name variable

        // If name equals, object are equal
        if (obj instanceof Demo) {
            return this.name.equals(((Demo)obj).name);
        }
        return false;
    }

    // ****** Badly Overrided hashcode *******
    public int hashcode() {
        // But you calculate hashcode using both the fields

        // You should never use this kind of code in hashcodes. 
        // Use complex algorithm which gives you distinct result for 
        // objects that are not equal.
        return str.length + name.length;
    }
}

So, If the two objects have same name, then they would be equal, but still if their str field has different length, then their hashcodes will be different.

That is why, you should always use the same fields in equals and hashcode calculation.

Community
  • 1
  • 1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 1
    If I am able to compare two objects using equals as shown in the above example, why should I override hashcode method? – Anand Oct 13 '12 at 18:16
  • 1
    Because hash-based collections (like HashMap and HashSet) use hashCode to first select a bucket, and then compare the argument to every element in the bucket. That's what makes them so efficient: even if the map contains 10000 elements, thanks to the hashCode, the map will only have to compare the argument with 0, 1 or 2 other objects that are in the same bucket. – JB Nizet Oct 13 '12 at 18:17
  • @anand. See my edit. The hashcode method of `Object` class, assign the hashcodes based on some algorithm which it uses for `equals` method also.. So, if you change one algorithm , you must change accordingly the other, to make them work in contract. – Rohit Jain Oct 13 '12 at 18:17
  • @AmitD. That's why I have written there, `Badly Overrided HashCode`. – Rohit Jain Oct 13 '12 at 18:33
  • better to use getClass than instanceof in equals method – Anand Oct 13 '12 at 18:55
  • @anand. `getClass` returns the type of `reference` not the type of `instance`. You need to use `instanceof` here, which checks the actual instance. – Rohit Jain Oct 13 '12 at 18:57
  • @RohitJain: no. getClass() is a polymorphic method, and returns the actual class of the object. – JB Nizet Oct 13 '12 at 19:42
  • @JBNizet Oh. Then is there any valid reason why we should use `instanceof` or `getClass`?? – Rohit Jain Oct 13 '12 at 19:43
  • To make the equals method respect the contract `A.equals(B) iff B.equals(A)`. Using instanceof, you can have `A instanceof B`, and `!(B instanceof A)`. This is a controversial problem though. Read more about it at http://stackoverflow.com/questions/596462/any-reason-to-prefer-getclass-over-instanceof-when-generating-equals. – JB Nizet Oct 13 '12 at 19:48
  • @JBNizet Thanks :) I was reading `Effective Java`. And have just reached that part. – Rohit Jain Oct 13 '12 at 19:51
  • @JBNizet chrs mate - i have two further questions based on your answer: (1) if one is not using a hash based collection does one still have to override a hashcode method, (2) is a List a hash based collection? chrs mate. – BenKoshy Nov 25 '16 at 04:47
  • @BKSpurgeon 1. yes, because unless the class is private, and you don't mess up, nothing prevents it to be used in a hash-based collection later, even if it's not the case now. Why make the code fragile when it's easy to make it robust?. 2. List is an interface, so ite depends on the implementation, but I don't know (nor can imaging) any List implementation using hashCode. – JB Nizet Nov 25 '16 at 08:09
0

You'll need to also override hashCode to get the behavior that you expect. The default implementation of Object.hashCode is probably returning the object references, although according to the docs this isn't required.

Without overriding hashCode you can't expect specialized results; this is analogous to overriding equals.

pb2q
  • 58,613
  • 19
  • 146
  • 147
0

You should override hashcode methode. if a class overrides equals, it must override hashCode when they are both overridden, equals and hashCode must use the same set of fields if two objects are equal, then their hashCode values must be equal as well if the object is immutable, then hashCode is a candidate for caching and lazy initialization

Tushar Paliwal
  • 301
  • 4
  • 11