The equals method checks by default whether the object given as a parameter has the same reference as the object it is being compared to. In other words, the default behaviour checks whether the two objects are the same. If the reference is the same, the method returns true
, and false
otherwise.
For strings, equals
works as expected in that it declares two strings identical in content to be 'equal' even if they are two separate objects. The String class has replaced the defaultequals
with its own implementation.
This reliance on default methods such as equals
is actually the reason why Java demands that variables added to ArrayList and HashMap are of reference type.
Each reference type variable comes with default methods, such as equals, which means that you don't need to change the internal implementation of the ArrayList class when adding variables of different types.
In addition to equals
, the hashCode
method can also be used for approximate comparison of objects.
The method creates from the object a "hash code", i.e, a number, that tells a bit about the object's content.
If two objects have the same hash value, they may be equal. On the other hand, if two objects have different hash values, they are certainly unequal.
Hash codes are used in HashMaps, for instance. HashMap's internal behavior is based on the fact that key-value pairs are stored in an array of lists based on the key's hash value. Each array index points to a list. The hash value identifies the array index, whereby the list located at the array index is traversed. The value associated with the key will be returned if, and only if, the exact same value is found in the list (equality comparison is done using the equals method). This way, the search only needs to consider a fraction of the keys stored in the hash map.
We find the borrower when searching for the same object that was given as a key to the hash map's put
method. However, when searching by the exact same book but with a different object, a borrower isn't found, and we get the null reference instead. The reason lies in the default implementation of the hashCode
method in the Object
class. The default implementation creates a hashCode
value based on the object's reference, which means that books having the same content that are nonetheless different objects get different results from the hashCode method. As such, the object is not being searched for in the right place.
For the HashMap to work in the way we want it to, that is, to return the borrower when given an object with the correct content (not necessarily the same object as the original key), the class that the key belongs to must overwrite the hashCode
method in addition to the equals
method. The method must be overwritten so that it gives the same numerical result for all objects with the same content. Also, some objects with different contents may get the same result from the hashCode method. However, with the HashMap's performance in mind, it is essential that objects with different contents get the same hash value as rarely as possible.
We've previously used String
objects as HashMap keys, so we can deduce that the String
class has a well-functioning hashCode
implementation of its own. We'll delegate, i.e., transfer the computational responsibility to the String
object.
public int hashCode() {
return this.name.hashCode();
}
For a class to be used as a HashMap's key, we need to define for it:
equals
method, so that all equal or approximately equal objects cause the comparison to return true and all false for all the resthashCode
method, so that as few objects as possible end up with the same hash value