Java equals() and hashCode() Contract

java-hashcode

The Java super class java.lang.Object defines two important methods:

public boolean equals(Object obj)
public int hashCode()

In this post, I will first show an example of a common mistake, and then explain how the equals() and hashCode() contract works.

1. A common mistake

The common mistake is shown in the example below.

import java.util.HashMap;
 
public class Apple {
	private String color;
 
	public Apple(String color) {
		this.color = color;
	}
 
	public boolean equals(Object obj) {
		if(obj==null) return false;
		if (!(obj instanceof Apple))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Apple) obj).color);
	}
 
	public static void main(String[] args) {
		Apple a1 = new Apple("green");
		Apple a2 = new Apple("red");
 
		//hashMap stores apple type and its quantity
		HashMap<Apple, Integer> m = new HashMap<Apple, Integer>();
		m.put(a1, 10);
		m.put(a2, 20);
		System.out.println(m.get(new Apple("green")));
	}
}

In the main method, two apples (“green” and “red”) are created and put to a HashMap. However, when the map is asked to provide the green apple, the green apple is not found. The program above prints null. We are sure that the green apple is stored in the hashMap when inspecting the HashMap in the debugger.

What causes the issue?

2. Problem caused by hashCode()

The problem is caused by the un-overridden method “hashCode()”. The contract between equals() and hashCode() is:
1) If two objects are equal, then they must have the same hash code.
2) If two objects have the same hash code, they may or may not be equal.

The idea behind a Map is to be able to find an object faster than a linear search. Using hashed keys to locate objects is a two-step process. Internally, the HashMap is implemented as an array of Entry objects. Each Entry has a pair and a pointer pointing to the next Entry. The hash code of the key object is the index for addressing the array. This locates the linked list of Entries in that cell of the array. The linked list in the cell is then searched linearly by using equals() to determine if two objects are equal.

The default implementation of hashCode() in Object class returns distinct integers for different objects. Therefore, the second apple has a different hash code.

The HashMap is organized like a sequence of buckets. The key objects are put into different buckets. It takes time of O(1) to get to the right bucket because it is an array access with the index. Therefore, it’s a good practice to evenly distribute the objects into those buckets, i.e., having a hashCode() method that produces evenly distributed hash code. (Not the main point here though)

The solution is to add the hashCode method to the Apple class. Here I just use the color string’s length for demonstration.

public int hashCode(){
	return this.color.hashCode();	
}

21 thoughts on “Java equals() and hashCode() Contract”

  1. Yup! Also it is like saying how do you find the two apples similar or different. For me it is the color basically. For java, it is just the object address. Let java know you value the proprtery “color of the apple ”
    here as the distinguishing factor and the hashcode then matches the key as per the color of Apple. Hence the hashcode function as above. So if a new apple comes to add with the same color, the hashcode here will return true.

  2. The code is absolutely fine. You are assuming that the new Apple is always a new object which was never stored. However, hashmap checks if the apple is new or not based on hashcode and equals. For it, if you create 10 new apples and all of them are equal as per equals method and return same hascode, they all are same object for hashmap.

  3. It seems a good example.
    If we are overriding equals() but not hashcode(), then it calls the equals() of Object class which does reference comparison. In this example, System.out.println(m.get(new Apple(“green”))) has new reference… So, it gives null. If we pass System.out.println(m.get(a1)); it must returns an Object. String class equals() does content comparison. To achieve that implementation, we need to override both equals() and hashcode(). So that we can give priority to Subclass methods rather Object class equals(). If hashcode collision, it compares keys by equals(). If objects are same, It replaced old value with new one…It keys are different, it keeps multiple values in same entry by linked list concept. Hope, it gives you better clarity.

  4. dude, even after adding the hascode() method it will return null. Test your code please before posting it.

  5. HashMap call equals method before putting into it’s bucket and equals method is for content comparison so evenif it’s new Apple(“green”) content wise object is equal and should return the key already adde. Code is perfactly fine.

  6. System.out.println(m.get(new Apple(“green”))); You get null since you are creating a new Apple and never stored it into the hashmap. This code seems wrong to me.

  7. If 2 objects are equal, they MUST have the same hash code.

    If 2 objects have the same hash code, it doesn’t mean that they are equal.

    Overriding equals() alone will make your business fail with hashing data structures like: HashSet, HashMap, HashTable … etc.

    Overriding hashcode() alone doesn’t force java to ignore memory addresses when comparing 2 objects.

    Check this tutorial: http://programmergate.com/working-hashcode-equals-java/

  8. “The default implementation of hashCode() in Object class returns distinct integers for different objects”

    no, it doesn’t.

    Here is the javadoc for Object.hashCode()

    As much as is reasonably practical, the hashCode method defined by
    * class {@code Object} does return distinct integers for distinct
    * objects. (This is typically implemented by converting the internal
    * address of the object into an integer, but this implementation
    * technique is not required by the
    * Java; programming language.)

    Here’s the thing; objects get moved around during GC, so two different objects can have the same address — at different points in time. the hash value for a given object is “set” the first time you call the “hashCode()” method. so put it all together and the above statement is categorically false. I have personally seen multiple objects have the same hash value.

  9. HashMap is not an array of arrays, Its just one array of Entry objects, and Entry consists of a Key, Value pair.

  10. I think it would be helpfull to add that if you use same object reference as it was stored in map i.e. System.out.println(m.get(a1)); you will see the correct result. This explains that if hashCode is not overridden, a default implementation will be used which is always identical for same object. Having sailed that, you should always override hashCode method if object is going to be used in hash based collection

  11. Hi every one …

    Excuse me ,I am of a question , but I don’t know what is it it’s subject to ask..

    I wanna know that when the System.currentTimeMillis() in java will became zero , I mean when it will start again ?

  12. I find it tedious to implement equals() and hashCode() by hand. I also see too many mistakes in code in this area. Better to automate this, so I have just released a free open source tool VALJOGen (valjogen.41concepts.com) which can generate your value classes with setters/getters (*), Object.hashCode, Object.equals, Object.toString, Comparable.compareTo and more from plain java interfaces. The generated output can be automatically updated when you change your interfaces – no need to maintain the generated code. Let me know what you think?

  13. Hi, I’m learning Java and I’m trying to figure out the comparison of the equals method:
    return this.color == ((Apple) obj).color;

    you are comparing two strings with the == operator; don’t you have to use the equals method of the strings? Thank you

  14. If you execute the code without the hasCode implementation, it won’t even call the equals method. It will check the equals only when you are comparing the objects (using comparator or comparable…) or in the case, when the hashCode is implemented.

Leave a Comment