The ‘hashcode‘ and ‘equals’ method in java is common to all objects in the Java programming language very similar to ‘notify()’ , ‘notifyAll()’ and the ‘toString()’ method. These methods have to be overridden for hash collections to work. The ‘Hashset‘ for example is a java collection class used for eliminating duplicate elements. The following post explains how the Hashset works by leveraging ‘equals’ ‘and ‘hashcode‘. Oracle Java states the contract between ‘equals‘ and ‘hashcode‘ contract is as follows –
“Any two objects whose ‘hashcodes’ are same need not be equal in value. But two objects equal in value will always have the same hashcode.”
Let us take the following scenario. Two cities are considered equal if and only if both the name of city and name of country are same. If we consider just the city name, there could be two cities with same name but in different countries. They are not the same cities. They have be considered as separate cities because they are in two different countries but hold the same name. Usually Hashset will simply look for duplicates by name of city if it is a collection of city names. But in this scenario we need to overrirde the ‘equals’ method to define how two cities are considered equal by writing custom logic.
To understand the internal workings of a Hashset consider the Java objects below with two attributes – City name and Country where the city is located.
| City (Element 1) property: name, value: San Jose, property: country, value: United States |
| City (Element 2) property: name, value: New York, property: country, value: United States |
| City (Element 3) property: name, value: San Jose, property: country, value: Costa Rica |
| City (Element 4) property: name, value: Dubai property: country, value: UAE |
| City (Element 5) property: name, value: Sydney property: country, value: Australia |
This should be handled in the city class. The ‘equals‘ and ‘hashcode’ method have to be overridden. If we add try to add all five 5 city elements above to the hashset , using the overriding strategy below then the total count in Hashset will be 5. This means the Hashset considers ‘San Jose’ a unique city when there are two instances of cities with name equal to ‘San Jose’. Remember these cities have a different country name. Hence they cannot be considered equal objects. Hashset will therefore consider the cities to be unique.
// Case 1 - Wrong override
// "San Hose" considered unique city
// Override by city name only instead of city name
and name of country both together
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals (Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
City1 other = (City1) obj;
return Objects.equals(name, other.name);
}
To correct this, the overriding strategy should include the city name and the country name. Both together decide if a particular city is unique or not. Cities with similar names but located in different regions or countries have to be taken into account. The following snippet provides the correct overring strategy.
// Case 2 - "San Hose" considered a non-unique city name
// Correct override ---> Size=5 (All five cities are unique) non // Override by city name and name of country both together
@Override
public int hashCode() {
return Objects.hash(country, name);
}
@Override
public boolean equals (Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass()! = obj.getClass())
return false;
City2 other = (City2) obj;
return Objects.equals(country, other.country)
&& Objects.equals(name, other.name);
}
Now that we have properly overridden the city class based on both city and country attributes Hashset will display unique number of cities. The correct number of cities in this Hashset is 4.
Leave a Reply