Tuesday, December 27, 2011

Java how to Override equals() and hashCode() method

Object class has following five non final methods.

1. clone()
2. equals(Object obj)
3. finalize()
4. hashCode()
5. toString()

Every Java class has Object as a superclass so by default all above methods are provided in every java class. In this article we will discuss in detail the concepts of equals() and hashCode() method. We will discuss why it is necessary to override these methods and how we can override them.

Default implementation of equals() in Object class

The equals() method provided by Object class tests referential equality of objects and returns true if object references are equal.

Source code of Object class equals() method

public boolean equals(Object obj) {
            return (this == obj);
}

equals() method uses == operator to determine equality of objects.

Why to override equals() method

The equals() method provided in the Object class uses the identity comparison operator (==) to test if they are identical, which means they refer to the same objects and share the same address in memory. To test whether two objects are equal in the sense of equivalency (containing the same information), you must override the equals() method.

Overriding equals method in Java

Java has provided a contract to follow while overriding equals() method. An implementation of the equals() method must satisfy following properties of an equivalence relation.

Reflexive: For any reference self, self.equals(self) is always true.

Symmetric: For any references x and y, x.equals(y) is true if and only if y.equals(x) is true.

Transitive: For any references x, y and z, if both x.equals(y) and y.equals(z) are true, then x.equals(z) is true.

Consistent: For any references x and y, multiple invocations of x.equals(y) always return the same result, provided the objects denoted by these references have not been modified to affect the equals comparison. 

Null comparison: For any non-null reference obj, obj.equals(null) is always false.

Putting all together we will follow a standard approach given below to implement equals() method.

1. Do this check: If yes then return true.

2. Do null check: if yes then return false.

3. Check both objects belong to same class or not using obj.getClass() != this.getClass(): Return false if objects are not of same class.

4. Type cast the object and do equality comparison for the objects.

Try this code example

Book class with each book having title and author name.

Example 1: Using default Object class equals() method. Check objects references for equality.

public class Book {
            private String nameOfBook;
            private String author;
            public Book(String nameOfBook, String author) {
                        this.nameOfBook = nameOfBook;
                        this.author = author;
            }
            public static void main(String[] args) {
                        Book book1 = new Book("Alchemist", "Paule Calhoe");
                        Book book2 = new Book("Alchemist", "Paule Calhoe");
                        Book book3 = book1;
                        System.out.println("book1 equals book2 : " + book1.equals(book2));
                        System.out.println("book3 equals book1 : " + book3.equals(book1));
            }
}

Output:
book1 equals book2 : false because book1 and book2 are different references.
book3 equals book1 : true because book3 and book1 are same references.

Example 2: Overriding equals() method to check object state (the object’s data). In our example two Book objects are said to be equal if book and author name is same.

public class Book {
            private String nameOfBook;
            private String author;
            public Book(String nameOfBook, String author) {
                        this.nameOfBook = nameOfBook;
                        this.author = author;
            }
            public boolean equals(Object obj) {
                        // do this check
                        if (this == obj)
                                    return true;
                        // do null check
                        if (obj == null)
                                    return false;
                        // check both objects belongs to same class or not
                        if (getClass() != obj.getClass())
                                    return false;
                        // type cast the object
                        Book book = (Book) obj;
                        if (author == null) {
                                    if (book.author != null)
                                                return false;
                        } else if (!author.equals(book.author))
                                    return false;
                        if (nameOfBook == null) {
                                    if (book.nameOfBook != null)
                                                return false;
                        } else if (!nameOfBook.equals(book.nameOfBook))
                                    return false;
                        return true;
            }
            public static void main(String[] args) {
                        Book book1 = new Book("Alchemist", "Paule Calhoe");
                        Book book2 = new Book("Alchemist", "Paule Calhoe");
                        Book book3 = book1;
                        Book book4 = new Book("Alchemist", "Salman Rushdi");
                        System.out.println("book1 equals book2 : " + book1.equals(book2));
                        System.out.println("book3 equals book1 : " + book3.equals(book1));
                        System.out.println("book4 equals book2 : " + book4.equals(book2));
            }
}

Output:
book1 equals book2 : true because book and author name is same
book3 equals book1 : true because book3 and book1 are same references.
book4 equals book2 : false because author name is not same.

Reason to prefer getClass() over instanceof()

The reason to use getClass() is to ensure the symmetric property of the equals() contract.

Symmetric: For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true. By using instanceof(), it's possible to not be symmetric.

Consider the example: Dog extends Animal. Give Animal a and Dog d (with other fields the same).

a.equals(d) : true
d.equals(a) : false

This violates the symmetric property. To strictly follow equals() contract, symmetry must be ensured, and thus the class needs to be the same. So, getClass() is preferred over instanceof().

Mistake while overriding equals() method

A silly mistake is to overload equals() method instead of overriding it.  The signature of the equals() method is 

public boolean equals(Object obj) 

To make method overriding to occur, the signatures must be identical (with same visibility)

public boolean equals(Book book) will not override it. The argument to the equals() method must be an Object.

Let’s now discuss second part of this article and learn how to override hashCode() method.

Object class hashCode() method

The signature of hashCode is:

public int hashCode(). Returns a hash code value for the object.


The int value returned from hashCode() is of particular use with the hash based Collection classes e.g. HashTable, HashSet. The nature of hash based collections is to store keys and values. When storing objects in a hash, Java uses the hashCode() method which is a method that returns a hash code value for the object. The hashCode() method is supposed to return an int that should uniquely identify different objects. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.

Why always override hashcode() if overriding equals()?


If you override the equals(), you MUST also override hashCode() to make sure that if two objects are equal, then calling hashCode() on both objects must return the same value. Otherwise a violation of the general contract for Object.hashCode will occur which can lead to errors. Here is the contract, copied from the java.lang.Object specialization. The general contract of hashCode is:

1. 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. This integer need not remain consistent from one execution of an application to another execution of the same application.

2. 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.

3. It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

Overriding hashCode() method in Java

 

The various methods to override hashCode() method are as follows.


Override equals() and hashCode() In Eclipse and Netbeans


In Netbeans

1) Write your Class.
2) Right click + insert code + Generate equals() and hashCode().

In Eclipse

1) Write your Class.
2) Go to Source Menu + Generate hashCode() and equals()

Guideline to generate effective Hashcode
Joshua Bloch in Effective Java provides an good guidelines for generating an hashCode value.
1. Store some constant nonzero value; say 17, in an int variable called result.

2. For each significant field f in your object (each field taken into account by the
equals()), do the following

a. Compute an int hashCode c for the field.

   i.      If the field is a boolean, compute
              c = (f ? 1 : 0).
   ii.     If the field is a byte, char, short, or int, compute c = (int) f.
   iii.    If the field is a long, compute c = (int) (f ^ (f >>> 32)).
   iv.    If the field is a float, compute c = Float.floatToIntBits(f).
   v.     If the field is a double, compute
              long l = Double.doubleToLongBits(f),
              c = (int)(l ^ (l >>> 32))
   vi.    If the field is an object reference then equals() calls equals() for this field.           Compute c =  f.hashCode() 
   vii.   If the field is an array, treat it as if each element were a separate field. That             is, compute a hashCode for each significant element by applying above rules          to each element.                            
     
    b. Combine the hashCode c computed in step 2.a into result as follows:
       result = 37 * result + c;
3. Return result. 
Look at the resulting hashCode() method and make sure that equal instances have equal hash codes.

@Override
public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((author == null) ? 0 : author.hashCode());
            result = prime * result
                                    + ((nameOfBook == null) ? 0 : nameOfBook.hashCode());
            return result;
}
Generate hashcode using Apache HashCodeBuilder

Rather than using Eclipse generated equals() and hashCode() methods we could use the Apache Commons EqualsBuilder and HashCodeBuilder.

Both of these builders have a very useful reflection based equals / hashcode method You create a new EqualsBuilder object inside your equals method, append access to any properties of your object that make it unique and then call the isEquals() method to test the properties. You do a very similar thing for hashCode. 

@Override
public boolean equals(Object obj) {
    if (obj instanceof Book) {
        Book book = (Book) obj;
        EqualsBuilder builder = new EqualsBuilder();
        builder.append(getNameOfBook(), book.getNameOfBook());
        builder.append(getAuthor(), book.getAuthor());
        return builder.isEquals();
    }
    return false;
}
@Override
public int hashCode() {
    HashCodeBuilder builder = new HashCodeBuilder();
    builder.append(getNameOfBook());
    builder.append(getAuthor());
    return builder.toHashCode();
}

If you're really stuck... 

If you are unsure how to implement hashCode(), just always return 0 in your
implementation. So all of your custom objects will return the same hash
code. It is a bad implementation because it returns same hash code for all the objects. But you have implemented hashCode() correctly!

public int hashCode(){
    return 0;
}

Putting all this together

public class Book {
            private String nameOfBook;
            private String author;
            public Book(String nameOfBook, String author) {
                        this.nameOfBook = nameOfBook;
                        this.author = author;
            }
            @Override
            public int hashCode() {
                        final int prime = 31;
                        int result = 1;
                        result = prime * result + ((author == null) ? 0 :
                                    author.hashCode());
                        result = prime * result
                                    + ((nameOfBook == null) ? 0 : nameOfBook.hashCode());
                        return result;
            }
            @Override
            public boolean equals(Object obj) {
                        if (this == obj)
                                    return true;
                        if (obj == null)
                                    return false;
                        if (getClass() != obj.getClass())
                                    return false;
                        Book other = (Book) obj;
                        if (author == null) {
                                    if (other.author != null)
                                                return false;
                        } else if (!author.equals(other.author))
                                    return false;
                        if (nameOfBook == null) {
                                    if (other.nameOfBook != null)
                                                return false;
                        } else if (!nameOfBook.equals(other.nameOfBook))
                                    return false;
                        return true;
            }
            public static void main(String[] args) {
                        Book book1 = new Book("Alchemist", "Paule Calhoe");
                        Book book2 = new Book("Alchemist", "Paule Calhoe");
                        Book book3 = book1;
                        Book book4 = new Book("Alchemist", "Salman Rushdi");
                        System.out.println("book1 hashcode : " +
                                                book1.hashCode());
                        System.out.println("book2 hashcode : " +
                                                book2.hashCode());
                        System.out.println("book3 hashcode : " +
                                                book3.hashCode());
                        System.out.println("book4 hashcode : " +
                                                book4.hashCode());
                        System.out.println("book1 equals book2 : " +
                                                book1.equals(book2));
                        System.out.println("book3 equals book1 : " +
                                                book3.equals(book1));
                        System.out.println("book4 equals book2 : " +
                                                book4.equals(book2));
            }
}

Output:
book1 hashcode : 846556864
book2 hashcode : 846556864
book3 hashcode : 846556864
book4 hashcode : -456217454
book1 equals book2 : true so book1 and book2 hashcode is also equal.
book3 equals book1 : true so book3 and book1 hashcode is also equal.
book4 equals book2 : false so book4 and book2 hashcode is unequal.

That’s All Folks

Let us know your comments. Continue Reading!


4 Responses to “Java how to Override equals() and hashCode() method”

Javin @ Struts interview questions said...
December 27, 2011 at 7:24 PM

Good post Sandeep. One point you can also add that equals must be consistent with compareTo method otherwise when you store these object in collection like TreeMap or TreeSet they will not behave as expected like Set will not able to deny duplicates etc.
I have also shared my view as equals method with unit test and hashcode in Java . let me know how do you find it.


Mihir said...
March 28, 2012 at 8:24 PM

One of the best articles on overriding equals and hashCode methods.


Saran said...
August 16, 2012 at 5:23 PM

Very Simple and easy to understand.

Thanks for the post


Anonymous said...
September 17, 2012 at 3:43 PM

very good post.it will helps to the all the people


Post a Comment

© 2011 a2ztechguide.com • All rights reserved.
Blogger Template by Bloggermint