Learn in Java
 


   

Equals and HashCode

 
You might have seen a hundred articles on the internet on why you should override the hashcode method when you override the equals. But we are sure you might not be convinced or confused as to why you should be doing that. In the below article, we will try to explain the same in simpler terms and with our very famous angry birds example.

The reason why you should override hashcode whenever you override equals is that you need a way to create the same object you want to find. This is particularly useful when you are dealing with collections. Finding an object is a two step process :

	1. Find the bucket holding the object
	2. Compare the objects in the bucket with the object we are trying to find
Overriding hashCode() method provides a way to find the actual bucket in which the object is present. Overriding equals() method provides a way to compare and see if the object we are finding is same or not.

Remember:
	  1. You need a way to create the same object you want to find
	  2. hashCode() helps us to find the correct bucket
	  3. equals() helps us to compare the objects in the bucket

First, let us take a look the below example. Here, we are creating 3 AngryBird objects with different colors and adding it to a HashSet. Now, our goal is to find RED coloured bird. We will not override the hashCode() method as of now. Let us run the example and see the output,

EqualsAndHashCode.java : - Without overriding hashCode

package com.learninjava;

import java.util.Set;
import java.util.HashSet;

/**
 * @author learninjava.com
 * @see www.learninjava.com
 */
public class EqualsAndHashCode {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		AngryBird redBird = new AngryBird(1, "RED");
		AngryBird blueBird = new AngryBird(2, "BLUE");
		AngryBird blackBird = new AngryBird(3, "BLACK");
		
		Set<AngryBird> birdsSet = new HashSet<AngryBird>();
		birdsSet.add(redBird);
		birdsSet.add(blueBird);
		birdsSet.add(blackBird);
		
		
		AngryBird birdToFind = new AngryBird(1, "RED");
		System.out.println("HashCode of RED bird to find : " + birdToFind.hashCode() + "\n");
		
		System.out.println("HashCodes of birds in buckets...");
		for (AngryBird bird : birdsSet) {
			System.out.println("Color : " + bird.color + " HashCode : " + bird.hashCode() + "\n");
		}
		
		System.out.println("Is RED angry bird present in the buckets ? : " + birdsSet.contains(birdToFind));
	}
}

class AngryBird {

	public int size;
	
	public String color;

	public AngryBird(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public boolean equals(Object obj) {
		
		boolean flag = false;
		AngryBird angryBird = (AngryBird) obj;
		if (angryBird.size == size && 
				angryBird.color == color) {
			flag = true;
		}
		return flag;
	}
}

Output:
HashCode of RED bird to find : 1311053135

HashCodes of birds in buckets...
Color : BLACK HashCode : 2018699554

Color : RED HashCode : 366712642

Color : BLUE HashCode : 1829164700

Is RED angry bird present in the buckets ? : false

From the output, if we do not override the hashCode() method, the RED bird to find has a different hashCode than RED bird in the HashSet. This means there is no RED angry bird in the HashSet which is wrong. Now, let us override the hashCode() and see the output. Below is the updated example,



 

EqualsAndHashCode.java : - With overriden hashCode as zero

package com.learninjava;

import java.util.Set;
import java.util.HashSet;

/**
 * @author learninjava.com
 * @see www.learninjava.com
 */
public class EqualsAndHashCode {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		AngryBird redBird = new AngryBird(1, "RED");
		AngryBird blueBird = new AngryBird(2, "BLUE");
		AngryBird blackBird = new AngryBird(3, "BLACK");
		
		Set<AngryBird> birdsSet = new HashSet<AngryBird>();
		birdsSet.add(redBird);
		birdsSet.add(blueBird);
		birdsSet.add(blackBird);
		
		
		AngryBird birdToFind = new AngryBird(1, "RED");
		System.out.println("HashCode of RED bird to find : " + birdToFind.hashCode() + "\n");
		
		System.out.println("HashCodes of birds in buckets...");
		for (AngryBird bird : birdsSet) {
			System.out.println("Color : " + bird.color + " HashCode : " + bird.hashCode() + "\n");
		}
		
		System.out.println("Is RED angry bird present in the buckets ? : " + birdsSet.contains(birdToFind));
	}
}

class AngryBird {

	public int size;
	
	public String color;

	public AngryBird(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public int hashCode() {
		return 0;
	}

	public boolean equals(Object obj) {
		
		boolean flag = false;
		AngryBird angryBird = (AngryBird) obj;
		if (angryBird.size == size && 
				angryBird.color == color) {
			flag = true;
		}
		return flag;
	}
}

Output:
HashCode of RED bird to find : 0

HashCodes of birds in buckets...
Color : RED HashCode : 0

Color : BLUE HashCode : 0

Color : BLACK HashCode : 0

Is RED angry bird present in the buckets ? : true

Aah! After overriding the hashCode(), we now see that the hashcodes of RED angry birds matches. Also notice that now we can find the object in the HashSet.

Are we done ? Not yet, notice that we now have the same hashcode for all the angry birds. This is not a violation or does cause any harm. If you dont know how to override hashCode(), simply return zero as shown. However, an efficient hashCode algorithm must distribute the objects in the buckets evenly. To understand this, we will analyse using a diagram but before that let us override the hashCode with a better algorithm. Here is how we can do one,

EqualsAndHashCode.java : - With overriden and efficient hashCode

package com.learninjava;

import java.util.Set;
import java.util.HashSet;

/**
 * @author learninjava.com
 * @see www.learninjava.com
 */
public class EqualsAndHashCode {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		AngryBird redBird = new AngryBird(1, "RED");
		AngryBird blueBird = new AngryBird(2, "BLUE");
		AngryBird blackBird = new AngryBird(3, "BLACK");
		
		Set<AngryBird> birdsSet = new HashSet<AngryBird>();
		birdsSet.add(redBird);
		birdsSet.add(blueBird);
		birdsSet.add(blackBird);
		
		
		AngryBird birdToFind = new AngryBird(1, "RED");
		System.out.println("HashCode of RED bird to find : " + birdToFind.hashCode() + "\n");
		
		System.out.println("HashCodes of birds in buckets...");
		for (AngryBird bird : birdsSet) {
			System.out.println("Color : " + bird.color + " HashCode : " + bird.hashCode() + "\n");
		}
		
		System.out.println("Is RED angry bird present in the buckets ? : " + birdsSet.contains(birdToFind));
	}
}

class AngryBird {

	public int size;
	
	public String color;

	public AngryBird(int size, String color) {
		this.size = size;
		this.color = color;
	}

	public int hashCode() {
		return size;
	}

	public boolean equals(Object obj) {
		
		boolean flag = false;
		AngryBird angryBird = (AngryBird) obj;
		if (angryBird.size == size && 
				angryBird.color == color) {
			flag = true;
		}
		return flag;
	}
}

Output:
HashCode of RED bird to find : 1

HashCodes of birds in buckets...
Color : RED HashCode : 1

Color : BLUE HashCode : 2

Color : BLACK HashCode : 3

Is RED angry bird present in the buckets ? : true

Now since the hashCode returns the size of the bird rather than just a zero, observe that each bird has a different hash code according to their size. What is the advantage with this ? Let us look at the below diagram and see what happened on the heap in each of the above cases.

The number in the square brackets is the hashcode. The bird outside the buckets is the one we want to create as a key and match it with the one in the bucket. When we did not override the hashCode() method, the birds are distributed into separate buckets, but there is no way to find a particular bird as the hashCodes were not equal. When the hashCode was returning zeros, all the birds were squeezed into a single bucket. When the hashCode is overriden with efficient algorithm, the birds were distributed evenly and we could also find the bird we wanted.

Hope, after reading through this article, your search for the hashCode and equals ends... :)

 
   

Related Articles

Java 8 Stream
Java 8 Functional Interfaces
Java 8 Lambda Expressions
 
   

Recommended Articles

User and Daemon Threads
Thread States
Restful WebService using CXF
Restful Client using CXF
SOAP WebService using CXF
SOAP Client using CXF




LIKE/SHARE
 
 
 
Download Source:

Comments:


 

Top Picks

1. Top 5 websites offering free e-books

2. Top 10 websites for free Java Articles and Tutorials

3. Top 5 websites for Java Certifications

4. Top 5 Softwares for Java developers




   
   
   
 
 
© Copyright 2017. All rights reserved. All trademarks and logos belongs to their owners.         Website Counter