class Employee {
private String name;
public Employee(String name){
this.name = name;
}}
class EmployeeId {
private String id;
public EmployeeId(String id){
this.id = id;
}
public String toString(){
return id;
}}
public class HashCodeTest{
public static void main(String[] args) {
Map
employees.put(new EmployeeId("111"), new Employee("Top Gun"));
employees.put(new EmployeeId("222"), new Employee("Reader")); // Line A
employees.put(new EmployeeId("333"), new Employee("Million Dollar baby"));
Employee emp = employees.get(new EmployeeId("222")); // Line B
System.out.println(emp); // Line C
}
When we run this program the output at Line C will result in "null". Reason-> EmployeeId added at Line A is not the same EmployeeId, because our put creates a new Key with data as "222" and when we call get it creates a different EmployeeId at Line C. There is no way to tell or identify heap that the Object which we have put at Line A is same as the Object we want to get at Line B. Both are different Objects on heap, and default equals implementation (from Object class) will check for "==" comparison.
To overcome this we have to implement equals in our Key Object class which is EmployeeId i.e. Object identity.
class EmployeeId{
/* All the above implementation remains same, Adding equals to EmployeeId */
@Override
public boolean equals(Object obj){
if(obj == null)
return false;
if(obj.getClass() != getClass()){
return false;
}
EmployeeId empId = (EmployeeId)obj;
if(this.id == empId.id){
return true;
}
return false;
}}
We did a great job by implementing equals and now our code will pass equals and the key Object which we have put at Line A and get we are calling at Line B both are same. Run our HashCodeTest program, surprisingly result at Line C is still "null". HashMap is not perfroming as expected, hashing our object and returing the same value in constant time complexity is not working. What's wrong ?
The problem is Hashing is not working, Object class default hashCode implementation returns 32- bit memroy address as integer. The Object key at Line A is hashed to a different bucket (memory address) and when we call get at Line B it passes equals check, but this Key Object points to a different bucket and there is no value associated with the new bucket, which again results in "null".
We should also implement hashCode in our EmployeeId class to overcome this problem. HashCode implementation will retrun a code which is used for identifying bucket index.
class EmployeeId{
/* All the above implementation remains same, Adding hashCode to EmployeeId */
@Override
public int hashCode(){
return id.hashCode();
}}
Now run our HashCodeTest, Line C will print "Reader". Awesome, Our Key object has passed Object Identity check and is hashed to proper bucket.
Conclusions :-
1. equals and hashCode are mandatory requirement for your Objects.
2. If two objects are equal, their hash code must return same value.
3. We can even return some constant value from hashCode implementation. But in the case where Object identity fails (2 Objects are not equal), this causes a bucket collision.
