I have written a blog post about equals and hashCode contract. But now realised that this does not paint complete picture. There are some other scenario which needs to be covered.
Here is list of two videos which explain clearly about internal details about Hash based collection and why equals and hashCode contract is required in Java. I will try to explain all possible scenario that can be wrong when we don’t follow the contract.
Case 1 When class does nothing about equals and hashCode : When we do nothing to hashCode and equals method, each object will have their own default hashCode and these will be treated differently. Let’s assume if we have a HashSet of Employee object.
HashSet<Employee> employees = ….;
Employee key = new Employee….
When we try to fetch employee from employees, it will always return null. It is because of the fact that each Employee object will have unique hashSet(as we have not overriden hashCode method). When we try to find employee with key, there will be a very good probability that it maps to different bucket and will never be able to fetch the correct value. It is like finding a person’s home on wrong street.
Case 2 When class overrides equals but not hashCode : It will not work because of the reason given above. equality with equals is checked, after going to a bucket. If we are in wrong bucket then even if equality returns true we are fetching wrong object. It is like two person having same house number but in two different address. If we could not find correct street number there will always be a good chance that we deliver our mail to wrong person.
Case 3 When class overrides hashCode wrong but forget to override equals : Assume a case when we override hashCode to return a constant value(Say 100) for all objects and don’t override equals method. In this scenario, all values in the collection will map to a single bucket. When two object maps to same bucket, Java maintains a liked list to store these. So, in this scenario, we will end up with a linked list, which is inefficient. Lets check what happens when we try to fetch the value from linked list. As all object has same hashCode we will be able to reach the correct bucket, but it will always fails to do equality comparison(as Objects default equality checks memory address and two objects will never have same memory address).
Case 3 When class overrides hashCode wrong but override equals correctly: Assume a case in which we override equals correctly but always return constant value(say 100) from hashCode. This will work correctly but the created Map will be inefficient as all value will be mapped to a single bucket and hence we virtually end with a Linked list.
Case 4 Correct implementation of hashCode and equals: This works correctly as computed hashCode and equality will solely depends on the state of the object and whenever we create new object with same value it will end with same hashCode and hence same bucket.