equals and hashCode in Java

equals method is used to check the equality of two objects based on it’s contents.

equals versus ==

If two references point to the same object then only “==” returns true
For example:

String str1 = new String(“abc”);
String str2 = str1; // Here str1 and str2 are pointing to the same Object
String str3 = new String(“abc”);//str3 is pointing to another Object
System.out.println(“str1 == str2 :”+ (str1 == str2));// this will print true
System.out.println(“str1 == str3 :”+ (str1 == str3));// this will print false

But equals checks for equality of the Objects. String class’s equals method returns true if both the Strings contain same sequence of characters. For the above example the content for both the String Objects is same i.e., “abc”

System.out.println(“str1.equals(str2) :”+ (str1.equals(str2)));// this will print true
System.out.println(“str1.equals(str3) :”+ (str1.equals(str3)));// this will print true

Note: if objectRef1 == objectRef2 returns true, then objectRef1.equals(objectRef2) must return true.

hashCode

This method returns a hash code value for the object. The hash code is used by hash based collection types such as HashMap for storing and retrieving the entries .

equals and hashCode Contract

#1. If equals returns true, then the hashcode of the two objects must be same.
#2. If equals returns false, hashcode may or may not be same.
#3. If hashcode of two objects is same, then equals may or may not return true.
#4. If hashcode of two objects is not same, then equals must return false.

Overriding equals and not hashCode will break the contract between them.

Let us take the below example wherein we have overridden equals but not hashCode.

Note:hashCode method defined by Object class returns distinct integers for distinct objects.

package com.blog;
public class Employee {

private String name;

public Employee(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

public static void main(String[] args) {
Employee emp1 = new Employee(“Gyan”);
Employee emp2 = new Employee(“Gyan”);
System.out.println(“hashCode of emp1:”+emp1.hashCode());
System.out.println(“hashCode of emp2:”+emp2.hashCode());
System.out.println(“equals:”+emp1.equals(emp2));
System.out.println(“hashcodes are same:”+ (emp1.hashCode() == emp2.hashCode()));
}
}

Output
hashCode of emp1:366712642
hashCode of emp2:1829164700
equals:true
hashcodes are same:false // It breaks contract number #1

Problems caused in HashMap if we don’t override equals and hashCode

For understanding working of HashMap , please visit HashMap.

Let’s say we override the equals method but not the hashCode.

In the below example we will put a Employee,location entry in a map by using the original object and then try to retrieve the location using a similar Object.

When inserting an object into a hastable you use a key. The hash code of this key is calculated, and used to determine  the bucket to store the object internally. When you need to lookup an object in a hashtable you also use a key. The hash code of this key is calculated and used to determine the bucket to search for the object. Then the equals method is used to get the entry from the identified bucket.

Bucket index= (n – 1) & hashcode of key.  n-> number of buckets

Therefore when we put employeeMap.put(emp1, “Bhubaneswar”);
n=16 by default
hashCode of emp1:366712642
Bucket index =(16-1) & 366712642=10

While retrieving, we will use emp2.
hashCode of emp2:1829164700
Bucket index =(16-1) & 1829164700=12

So we will not get the entry using emp2 as it will be searching in Bucket-12 whereas the entry was stored in Bucket-10

package com.blog;

import java.util.HashMap;
import java.util.Map;

public class Employee {

private String name;

public Employee(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

public static void main(String[] args) {
Employee emp1 = new Employee(“Gyan”);
Employee emp2 = new Employee(“Gyan”);
Map<Employee, String> employeeMap = new HashMap<>();
employeeMap.put(emp1, “Bhubaneswar”);
System.out.println(“Employee location using original object:”+employeeMap.get(emp1));
System.out.println(“Employee location using similar object:”+employeeMap.get(emp2));
}
}

Output
hashCode of emp1:366712642
hashCode of emp2:1829164700
equals:true
hashcodes are same:false
Employee location using original object,emp1:Bhubaneswar
Employee location using similar object,emp2:null

Let us take another example wherein we have override hashCode but not equals.

Note:In the Object class, for any non-null reference values x and y, the equals method returns true if and only if x and y refer to the same object (x == y has the value true).

As the hashCodes of both emp1 and emp2 are same. So same Bucket will be identified for storing and retrieving the entry. But as equals method is not overriden, the entry will not be found in the Bucket.

package com.blog;
import java.util.HashMap;
import java.util.Map;
public class Employee {
private String name;
public Employee(String name) {
this.name = name;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}public static void main(String[] args) {
Employee emp1 = new Employee(“Gyan”);
Employee emp2 = new Employee(“Gyan”);
Map<Employee, String> employeeMap = new HashMap<>();
employeeMap.put(emp1, “Bhubaneswar”);
System.out.println(“hashCode of emp1:”+emp1.hashCode());
System.out.println(“hashCode of emp2:”+emp2.hashCode());
System.out.println(“equals:”+emp1.equals(emp2));
System.out.println(“hashcodes are same:”+ (emp1.hashCode() == emp2.hashCode()));
System.out.println(“Employee location using original object,emp1:”+employeeMap.get(emp1));
System.out.println(“Employee location using similar object,emp2:”+employeeMap.get(emp2));
}
}Output
hashCode of emp1:2234590
hashCode of emp2:2234590
equals:false
hashcodes are same:true
Employee location using original object,emp1:Bhubaneswar
Employee location using similar object,emp2:null

Let us now override both equals and hashCode.

package com.blog;
import java.util.HashMap;
import java.util.Map;public class Employee {private String name;public Employee(String name) {
this.name = name;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}public static void main(String[] args) {
Employee emp1 = new Employee(“Gyan”);
Employee emp2 = new Employee(“Gyan”);
Map<Employee, String> employeeMap = new HashMap<>();
employeeMap.put(emp1, “Bhubaneswar”);
System.out.println(“hashCode of emp1:”+emp1.hashCode());
System.out.println(“hashCode of emp2:”+emp2.hashCode());
System.out.println(“equals:”+emp1.equals(emp2));
System.out.println(“hashcodes are same:”+ (emp1.hashCode() == emp2.hashCode()));
System.out.println(“Employee location using original object,emp1:”+employeeMap.get(emp1));
System.out.println(“Employee location using similar object,emp2:”+employeeMap.get(emp2));
}
}Output
hashCode of emp1:2234590
hashCode of emp2:2234590
equals:true
hashcodes are same:true
Employee location using original object,emp1:Bhubaneswar
Employee location using similar object,emp2:Bhubaneswar

Leave a Reply

Your email address will not be published. Required fields are marked *