Java hashCode() and equals() Contract for the contains(Object o) Method of Set

The article is about hashCode and equals contract used for the contains(Object o) method in Set.

A puzzle about using the contains() method from Set

import java.util.HashSet;
 
class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}	
}
 
public class SetAndHashCode {
	public static void main(String[] args) {
		HashSet<Dog> dogSet = new HashSet<Dog>();
		dogSet.add(new Dog("white"));
		dogSet.add(new Dog("white"));
 
		System.out.println("We have " + dogSet.size() + " white dogs!");
 
		if(dogSet.contains(new Dog("white"))){
			System.out.println("We have a white dog!");
		}else{
			System.out.println("No white dog!");
		}	
	}
}

Output:

We have 2 white dogs!
No white dog!

We add two white dogs to the set - dogSet, and the size shows we have 2 white dogs. But why there is no white dog when we use contains() method?

The contains(Object o) method of Set

From Java Doc, the contains() method returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)). So the contains() method actually use equals() method to check equality.

Note that null can be added to a set as an element. The following code actually prints true.

HashSet<Dog> a = new HashSet<Dog>();
a.add(null);
if(a.contains(null)){
	System.out.println("true");
}

The public boolean equals(Object obj) method is defined in the Object class. Every class(including classes defined by yourself) has Object as a superclass and it is the root of any class hierarchy. All objects, including arrays, implement the methods of this class.

In the class defined by yourself, if you don't explicitly override this method, it will have a default implementation. It returns true if and only if two objects refer to the same object, i.e., x == y is true.

If we change the Dog class to the following, will it work?

class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}
 
	//overridden method, has to be exactly the same like the following
	public boolean equals(Object obj) {
		if (!(obj instanceof Dog))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Dog) obj).color);
	}
 
}

The answer is no.

Now the problem is caused by the hashCode and equals contract in Java. The hashCode() method is another method in Object class.

The contract is that if two objects are equal(by using equals() method), they must have the same hashCode(). If two objects have same hash code, they may be not equal.

The default implementation of public int hashCode() returns distinct integers for distinct objects. In this particular example, because we haven't defined our own hashCode() method, the default implementation will return two different integers for two white dogs! This breaks the contract.

Solution for the contains() method in Set

class Dog{
	String color;
 
	public Dog(String s){
		color = s;
	}
 
	//overridden method, has to be exactly the same like the following
	public boolean equals(Object obj) {
		if (!(obj instanceof Dog))
			return false;	
		if (obj == this)
			return true;
		return this.color.equals(((Dog) obj).color);
	}
 
	public int hashCode(){
		return color.length();//for simplicity reason
	}
}

References:

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html

Category >> Basics >> Common Methods  
If you want someone to read your code, please put the code inside <pre><code> and </code></pre> tags. For example:
<pre><code> 
String foo = "bar";
</code></pre>
  • fdfd

    thank you for the way you express the contract!!! really expressed by first making a mistake of only override equalTo();.

  • ryanlr

    Corrected. Thanks.

  • msfh

    jkkj;lk

  • aly

    Nice tutorial. I also check http://muhammadkhojaye.blogspot.co.uk/2010/02/java-hashing.html which also find useful.

  • Aldo

    return this.color == ((Dog) obj).color;

    Will not work either …