How to Override Equals, HashCode and CompareTo method in Java
Though modern IDE like Eclipse, IntelliJ or Netbeans allows you to generate equals, hashCode and compareTo methods for your value classes, it’s equally important, you know how to do that by hand. By overriding equals and hashcode method by yourself, you know how they work, what kind of errors you can get, and most importantly, it’s expected form you, as a Java programmer in any core Java interview. More often, you would see a coding question in Java, which ask you to override equals() , hashcode() , compare() and compareTo() methods for a value class.
Since I have already shared some tips on How to override compareTo method in Java, and couple of example of writing your own Comparator in Java, here I am sharing another simple example of overriding equals and hashCode methods.
If you know rules of overriding equals and hashCode , you might know that, whenever you override equals, you must have to override hashCode, otherwise your object will not behave properly on various collection classes e.g. Map or Set, which uses equals , compareTo , hashCode to implement there invariants e.g. Set implementations should not allow any duplicates.
Overriding Equals, HashCode and CompareTo in Java
In this Java program, we will override all three method for a Person class, which contains a String name, an integer id and a Date for date of birth. In order to override equals, you need to follow certain checks, e.g. checking null, checking type of object etc, Also your equals() method, when compared with null, should return false instead of throwing NullPointerException.
See this post for detailed tips on overriding equals method in Java. For overriding hashCode, you need to choose a prime, usually 31, but you can also choose other prime numbers e.g. 37, 17 etc. The reason for choosing these prime numbers are to generate a uniquely distributed hashcode, which eventually helps to avoid collision, when used in hash based collections like Hashtable and HashMap.
Another worth noting thing is using all variables, used in equals , in hashCode method to keep equals and hashCode consistent, and adhere to rules of overriding hashCode in Java. I have already discussed about Comparable and compareTo method, while sorting list of objects in Java.
A simple implementation of compareTo must return negative number if current object is lesser than provided object, positive if current object is greater in order than provided and zero if both objects are equal. Another worth noting point is that, compareTo() and equals() must be consistent with each other, otherwise your object will not behave properly on Collection classes, which uses compareTo() for sorting e.g. TreeSet or TreeMap. Anyway, here is a simple example of overriding equals, hashcode and compareTo in Java.
Example of Equals, HashCode and CompareTo in Java
In this sample example of overriding equals, hashcode and compareTo method, we will use a class named Person which has 3 properties String name, int id and Date to represent date of birth.
We will also use Generics along with Comparable to providing a type safe implementation. Remember we have used getClass() method instead of i nstanceof operator, which means a Person class cannot be equal to its subclass, this could create problem if you are using this class in EJB or any application server, where there is a chance that same class is loaded by two separate class loader.
On those cases it’s better to use instanceof operator because it will allow a Class to be equal to its subclass if rest of properties matched. This is also true for framework like Hibernate, which provides proxy implementation, which is essentially sub class of entity classes. In short, use instanceof if your class can be loaded by multiple class loader or it can be used by framework to create proxies.
/** * Simple Java Class to represent Person with name, id and date of birth. * * @author Javin Paul */ public class Person implements ComparablePerson>< private String name; private int id; private Date dob; public Person(String name, int id, Date dob) < this.name = name; this.id = id; this.dob = dob; > @Override public boolean equals(Object other)< if(this == other) return true; if(other == null || (this.getClass() != other.getClass()))< return false; > Person guest = (Person) other; return (this.id == guest.id) && (this.name != null && name.equals(guest.name)) && (this.dob != null && dob.equals(guest.dob)); > @Override public int hashCode()< int result = 0; result = 31*result + id; result = 31*result + (name !=null ? name.hashCode() : 0); result = 31*result + (dob !=null ? dob.hashCode() : 0); return result; > @Override public int compareTo(Person o) < return this.id - o.id; > >
Sample JUnit test case for testing Equals and HashCode
Here is simplest of simple test case to verify equals and hashCode in Java. Remember this unit test is not enough, if you want to verify all properties of equals and hashCode. If you should write test cases to check if it verify key properties of equals and hashcode method e.g. if a equal b then b should also be equal to a, or if a a equals b and b equals c then a and c should also be equal to each other. You must also verify whether compareTo() return 0 for equal object and return non zero value for non equal objects.
import java.util.Date; import org.junit.Test; import static org.junit.Assert.*; /** * Simple example of using equals and hashCode method * @author Javin Paul */ public class EqualsAndHashCodeTest < @Test public void testEquals()< Person james = new Person("James", 21, new Date(1980,12, 1)); Person same = new Person("James", 21, new Date(1980,12, 1)); Person similar = new Person("Harry", 21, new Date(1981,12,1)); assertTrue(james.equals(same)); assertTrue(james.hashCode() == same.hashCode()); assertFalse(james.equals(null)); assertFalse(james.equals(similar)); assertTrue(james.hashCode() != similar.hashCode()); > >
That’s all on this simple example of overriding equals, hashcode and compareTo method in Java. As I said, these test are just to verify that equals and hashCode methods are working, you can add more test to verify compareTo , and other behaviors of all three methods.
Java Comparator Interface
Java Comparator interface is used to sort an array or List of objects based on custom sort order. The custom ordering of items is imposed by implementing Comparator’s compare() method in the objects.
1. When to Use Comparator Interface
Java Comparator interface imposes a total ordering on the objects which may not have a desired natural ordering.
For example, for a List of Employee objects, the natural order may be ordered by employee’s id. But in real-life applications, we may want to sort the list of employees by their first name, date of birth or simply any other such criteria. In such conditions, we need to use Comparator interface.
We can use the Comparator interface in the following situations.
- Sorting the array or list of objects, but NOT in natural order.
- Sorting the array or list of objects where we can not modify the source code to implement Comparable interface.
- Using group by sort on list or array of objects on multiple different fields.
2. Overriding compare() Method
To enable total ordering on objects, we need to create a class that implements the Comparator interface. Then we need to override its compare(T o1, T o2) method.
The compare() compares its two arguments for order. It returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0 .
For a given Employee class, the order by employee name can be imposed by creating a Comparator like below.
import java.util.Comparator; public class NameSorter implements Comparator < @Override public int compare(Employee e1, Employee e2) < return e1.getName().compareToIgnoreCase( e2.getName() ); >>
import java.time.LocalDate; public class Employee
3.1. Collections.sort() and Arrays.sort()
- Use Collections.sort(list, Comparator) method sort a list of objects in order imposed by provided comparator instance.
- Use Arrays.sort(array, Comparator) method sort an array of objects in order imposed by provided comparator instance.
This utility method accepts a function that extracts a sort key for the class. This is essentially a field on which the class objects will be sorted.
//Order by name Comparator.comparing(Employee::getName); //Order by name in reverse order Comparator.comparing(Employee::getName).reversed(); //Order by id field Comparator.comparing(Employee::getId); //Order by employee age Comparator.comparing(Employee::getDate);
This utility method is used for group by sort. Using this method, we can chain multiple comparators to sort the list or array of objects on multiple fields.
It is very similar to SQL GROUP BY clause to order rows on different fields.
//Order by name and then by age Comparator.comparing(Employee::getName) .thenComparing(Employee::getDob); //Order by name -> date of birth -> id Comparator.comparing(Employee::getName) .thenComparing(Employee::getDob) .thenComparing(Employee::getId);
Using the above syntax, we can create virtually any sorting logic.
This utility method returns a comparator that imposes the reverse of the natural ordering or total ordering on a collection of objects that implement the Comparable interface.
//Reverse of natural order as specified in //Comparable interface's compareTo() method Comparator.reversed(); //Reverse of order by name Comparator.comparing(Employee::getName).reversed();
3.5. Other Collection Classes
Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps) to provide an ordering that is not natural ordering.
SortedSet sortedUniqueEmployees = new TreeSet(new NameSorter());
4. Java Comparator Examples
4.1. Sorting List of Custom Objects
Java example to sort a list of employees by name using Comparator.
ArrayList list = new ArrayList<>(); //Sort in reverse natural order Collections.sort(list, new NameSorter());
4.2. Sort List in Reverse Order
Java example to sort a list of employees by name using Comparator in reverse order.
ArrayList list = new ArrayList<>(); Collections.sort(list, Comparator.comparing( Employee::getName ).reversed());
Java example to sort a list of employees on multiple fields i.e. field by field.
ArrayList list = new ArrayList<>(); Comparator groupByComparator = Comparator.comparing(Employee::getName) .thenComparing(Employee::getDob) .thenComparing(Employee::getId); Collections.sort(list, groupByComparator);
In this tutorial, we learned about Comparator interface of Java collection framework. It helps in imposing a total order on objects without any change to the source code of that class.
We learned to sort list and array of custom objects. We also learned to reverse sort and implement group by sort in Java.