A 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 correctly, almost all of these questions come from beginners, who have either taken some Java programming classes or started learning Java by their own.
Though the 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 forces them to explore that part.
I have already written a couple of articles on equals and hashcode, like 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.
If you are doing Java programming than you probably know that every class in Java implicitly inherits 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 the 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 objects e.g. Order, Trade, Message can be compared to each other and you need a logical comparison.
One of the popular examples is java.lang.String class, which needs logical comparison i.e. character-based comparison. If two String object contains the same characters in the same order they are considered equals, which is what you need in many programming tasks.
Similarly, all domain object has equality defined, but the true need for equals and hashcode arise, when you use them as key in hash-based collection e.g. Hashtable or HashMap. These collection classes rely on rules of Java programming around equals and hashcode to work according to their specification, popularly known as an equals-hashcode contract.
According to which, you must override hashcode if you are overriding equals and vice-versa. The problem is that this is not enforced by the compiler, and if you make such a mistake, your program will not work properly.
For example, any object which doesn't follow equals and hashcode contract, if used as a 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 the 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.
Now there are multiple ways to override toString() in Java, see that link for some easy and productive way. For example, if you print an 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 the Arrays.toString() method. This will now show you elements stored in an array, instead of just the type of array and its hashcode.
I hope this helps you to understand the significance of equals, hashcode, and toString method in Java. In fact all methods of java.lang.Object are worth reading. These are fundamental concepts and solid knowledge of this will only be going to help you, both during the Interview and on Job.
Time spent on understanding java.lang, java.util and java.io are the best investments 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.
Though the 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 forces them to explore that part.
I have already written a couple of articles on equals and hashcode, like 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() are 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 of by equals method.If you are doing Java programming than you probably know that every class in Java implicitly inherits 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 the 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 objects e.g. Order, Trade, Message can be compared to each other and you need a logical comparison.
One of the popular examples is java.lang.String class, which needs logical comparison i.e. character-based comparison. If two String object contains the same characters in the same order they are considered equals, which is what you need in many programming tasks.
Similarly, all domain object has equality defined, but the true need for equals and hashcode arise, when you use them as key in hash-based collection e.g. Hashtable or HashMap. These collection classes rely on rules of Java programming around equals and hashcode to work according to their specification, popularly known as an equals-hashcode contract.
According to which, you must override hashcode if you are overriding equals and vice-versa. The problem is that this is not enforced by the compiler, and if you make such a mistake, your program will not work properly.
For example, any object which doesn't follow equals and hashcode contract, if used as a 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 the 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 do you need to override the toString method?
You should override the toString() method for all domain object, because whenever you print them using logger or System.out.println() statements, their 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 an 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 the Arrays.toString() method. This will now show you elements stored in an array, instead of just the type of array and its hashcode.
I hope this helps you to understand the significance of equals, hashcode, and toString method in Java. In fact all methods of java.lang.Object are worth reading. These are fundamental concepts and solid knowledge of this will only be going to help you, both during the Interview and on Job.
Time spent on understanding java.lang, java.util and java.io are the best investments 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.
Apart from the HashMap use, why you do need to override hashcode? What are those contracts you pointed out?
ReplyDelete@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.
ReplyDeleteGood 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.
ReplyDeleteAnother 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.
@AnObfuscator,
ReplyDeleteGood 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.