Why do I need to override the equals and hashCode methods in Java?
Joshua Bloch says on Effective Java
You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.
Let's try to understand it with an example of what would happen if we override equals()
without overriding hashCode()
and attempt to use a Map
.
Say we have a class like this and that two objects of MyClass
are equal if their importantField
is equal (with hashCode()
and equals()
generated by eclipse)
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
Imagine you have this
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
Override only equals
If only equals
is overriden, then when you call myMap.put(first,someValue)
first will hash to some bucket and when you call myMap.put(second,someOtherValue)
it will hash to some other bucket (as they have a different hashCode
). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.
Although it is not necessary to override equals()
if we override hashCode()
, let's see what would happen in this particular case where we know that two objects of MyClass
are equal if their importantField
is equal but we do not override equals()
.
Override only hashCode
If you only override hashCode
then when you call myMap.put(first,someValue)
it takes first, calculates its hashCode
and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue)
it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).
But the problem is that equals was not redefined, so when the map hashes second
and iterates through the bucket looking if there is an object k
such that second.equals(k)
is true it won't find any as second.equals(first)
will be false
.
Hope it was clear
java - what happens if hashCode is not overriden?
If you don't override hashcode() then the default implementation in Object class will be used by collections. This implementation gives different values for different objects, even if they are equal according to the equals() method.
Some collections, like HashSet, HashMap or HashTable use the hash code to store its data and to retrieve it. If you don't implement hashcode() and equals() in a consistent manner, then they will not function properly.
Edit:
As per Javadoc: Object.hashcode() is ''typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java(TM) programming language''. Therefore I would advise not to rely on a specific implementation. For what the implementations really do, see this answer to a similar question.
What is an object's hash code if hashCode() is not overridden?
Typically, hashCode() just returns the object's address in memory if you don't override it.
From 1:
As much as is reasonably practical, the hashCode method defined by class 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 JavaTM programming language.)
1 http://java.sun.com/javase/6/docs/api/java/lang/Object.html#hashCode
What happens if we override only hashCode() in a class and use it in a Set?
James Large answer is incorrect, or rather misleading (and part incorrect as well). I will explain.
If two objects are equal according to their equals() method, they must also have the same hash code.
If two objects have the same hash code, they do NOT have to be equal too.
Here is the actual wording from the java.util.Object documentation:
- If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
- It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
It is true, that if two objects don't have the same hash then they are not equal. However, hashing is not a way to check equality - so it is wildly incorrect to say that it is a faster way to check equality.
Also, it is also wildly incorrect to say the hashCode function is an efficient way to do anything. This is all up to implementation, but the default implementation for hashCode of a string is very inefficient as the String gets large. It will perform a calculation based on each char of the String, so if you are using large Strings as keys, then this becomes very inefficient; moreso if you have a large number of buckets.
In a Map (HashSet uses a HashMap internally), there are buckets and in each bucket is a linked list. Java uses the hashCode() function to find out which bucket it belongs in (it actually will modify the hash, depending on how many buckets exist). Since two objects may share the same hash, it will iterate through the linked list sequentially next, checking the equals() method to see if the object is a duplicate. Per the java.util.Set documenation:
A collection that contains no duplicate elements.
So, if its hashCode() leads it to a bucket, in which that bucket contains an Object where the .equals() evaluates to true, then the previous Object is overwritten with the new Object. You can probably view here for more information:
How does a Java HashMap handle different objects with the same hash code?
Generally speaking though, it is good practice that if you overwrite the hashCode function, you also overwrite the equals function (if I'm not mistaken, this breaks the contract if you choose not to).
What if in java I override equals() but not hashCode()?
If a.equals(b)
is true
, a.hashCode() == b.hashCode()
must also be true. If it's not the case, adding a
to a HashSet
and then checking if set.contains(b)
will return false
even though the Set
contains a
, which is equal to b
.
That's why the contract of hashCode()
(in Object
class) states that:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
HashSet allows duplicate item insertion if hashCode() is not overridden
It kind of is documented. See the documentation for java.lang.Object, where it says on hashCode()
:
If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
Additionally the following is found in the documentation for the Object.equals(Object)
method:
Note that it is generally necessary to override the hashCode method
whenever this method is overridden, so as to maintain the general
contract for the hashCode method, which states that equal objects must
have equal hash codes.
In other words, if with your class when instanceA.equals(instanceB) == true
and instanceA.hashCode() != istanceB.hashCode()
you are in fact violating the contract of the Object class.
Related Topics
In Java, How to Write a String Literal Without Having to Escape Quotes
Java Filewriter with Append Mode
How to Set Output Stream to Textarea
Javafx Controller Class Not Working
Java Thread.Sleep Puts Swing UI to Sleep Too
How to Re-Run Failed Junit Tests Immediately
Why Does the Behavior of the Integer Constant Pool Change at 127
In Java How to Sort One List Based on Another
How to Use Cardlayout with Netbeans Gui Builder
What Is Short Circuiting and How Is It Used When Programming in Java
No @Xmlrootelement Generated by Jaxb
How to Resize an Image Using Java
How to Return a JSON Object from a Java Servlet
In Java, How to Parse Xml as a String Instead of a File
Java Sending and Receiving File (Byte[]) Over Sockets
Easiest Way to Merge a Release into One Jar File