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:
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
Overridden equals(Object ob) :-
if(obj!=null){
if(obj instanceOf Dog){
return this.color.equals(((Dog)ob).color);
}
}
return false;
————————————————–>
Overridden hashCode():-
this.color.hashCode(); //String class is already override hashCode method and provide equality on the basis of value.
please give reply ๐
thank you for the way you express the contract!!! really expressed by first making a mistake of only override equalTo();.
Corrected. Thanks.
jkkj;lk
Nice tutorial. I also check http://muhammadkhojaye.blogspot.co.uk/2010/02/java-hashing.html which also find useful.
return this.color == ((Dog) obj).color;
Will not work either …