Thursday, January 12, 2017

Why Override equals, hashcode and toString method in Java

Couple of questions, which are often asked to me was why do we need to override equals() and hashcode() method, Why should I implement toString(), What will happen if I don't override them or in a different way, I have never overridden equals and hashcode and not faced any problem, why should I override them now. You guessed it correct, almost all of these questions comes from beginners, who have either taken some Java programming classes, or started learning Java by their own. Though concept of equality is something you cannot ignore, correct understanding of equals(), hashcode(), toString() and some other method from java.lang.Object class, often goes unnoticed, at least until they self-realize it or Interviewer force them to explore that part. I have already written couple of articles on equals and hashcode, e.g. equals vs ==,  hashcode tips, equals and hashcode interview questions and one of my personal favorite, 5 tips to override equals in Java, which already touches this subject but I thought to explain it clearly here for once and all.

Why you should override equals or hashcode

From the face, you can guess that equals() is used to check if two objects are equal or not. Now this equality can be defined in two ways, identity equality and logical equality, as I explained in equals vs == post, it's the logical equality, which is taken care by equals method.

If you are doing Java programming than you probably know that every class in Java implicitly inherit from java.lang.Object, and from there every object inherit equals() and hashcode(). There default implementation is in line with == operator, i.e. equals() provide identity equality and return true if reference variable pointing to same object. Now, if you don't need logical equality, then you don't need to override equals, but the problem is you will need it. All your domain object e.g. Order, Trade, Message can be compared to each other and you need logical comparison. One of the popular example is java.lang.String class, which needs logical comparison i.e. character based comparison. If two String object contains same characters in same order they are considered equals, which is what you need in many programming task. Similarly, all domain object has equality defined, but true need of equals and hashcode arise, when you use them as key in hash based collection e.g. Hashtable or HashMap. These collection classes relies on rules of  Java programming around equals and hashcode to work according to their specification, popularly known as equals-hashcode contract. According to which, you must override hashcode, if you are overriding equals and vice-versa. Problem is that this is not enforced by compiler, and if you make such mistake, your program will not work properly.

For example, any object which doesn't follows equals and hashcode contract, if used as key in HashMap, you may not be able to retrieve object again, see how HashMap works internally in Java for more details. In short, you need to override equals and hashcode, if you are writing a domain object, or you want to store them in hash based collection. Once you understand why you should override equals and hashcode, and when you should do that, it's easy to actually do that. See my post 5 tips to override equals and hashcode in Java for more details.

Why you need to override toString method

You should override toString() method for all domain object, because whenever you print them using logger or System.out.println() statements, there toString() method is called. Since default implementation of toString() is not very helpful, and only print classname@hashcode e.g. com.test.User@1033203. If you print some useful information, e.g. Arun, 1022320,  it will only help you during debugging and troubleshooting. Now there are multiple ways to override toString() in Java, see that link for some easy and productive way. For  example, if you print array in Java you will not see any meaningful value, because it doesn't override toString() method, but you can still print arrays by using Arrays.toString() method. This will now show you elements stored in array, instead of just type of array and it's hashcode.
Overriding equals, hashcode and toString in Java

I hope this help you to understand significance of equals, hashcode and toString method in Java. In fact all methods of java.lang.Object are worth reading. These are fundamental concept and solid knowledge of this will only going to help you, both during Interview and on Job. Time spent on understanding java.lang, java.util and are best investment in learning Java. Always remember to override hashcode() if you are overriding equals() method and vice-versa. Failing to do so is not a compile time error but can create really subtle bugs which can take hours to debug and solve, for example your HashMap reduced to linked list due to frequent collision, you not able to retrieve object put on HashMap etc.

Further Reading
Complete Java Masterclass
Java Fundamentals: The Java Language
Java In-Depth: Become a Complete Java Engineer!
Java Programming Interview Exposed
Cracking the code interview - 189 qustions and solutions


Anonymous said...

Apart from the HashMap use, why you do need to override hashcode? What are those contracts you pointed out?

javin paul said...

@Anonymous, the contract says that if two objects are equal by equals method then there hashcode must be same. Yes, it's main use in Collection itself, because they are container of objects. For example, equals() and hashcode() is also used in HashSet because its actually backed by HashMap.

AnObfuscator said...

Good article, I think this would have been a good place for you to also explain the relationship with equals and compareTo from the Comparable interface. Having compareTo inconsistent with equals can lead to some bizarre behaviors in standard Java collections. Many times, I find it simpler to create a Comparator, rather than implementing Comparable correctly.

Another point: I've noticed some developers are unnecessarily scared by hashcode(). I guess bad memories of college algorithm courses have scarred some. While writing a good hash algorithm from the ground up can indeed be tricky, it is often straightforward to simply compose the hashcodes of the Java objects your class is using.

javin paul said...

Good points. The relationship between equals() and compareTo() is very important and I have written about it couple of times, including in my earlier guide of overriding compareTo() in Java. As you pointed out, BigDecimal in one of the class from JDK which has inconsistent equals() and compareTo().

Regarding hashcode(), I totally agree with you, it's better to use composition there to call respective JDK classes to generate hashcode for different types.

Post a Comment