The theory (for the language lawyers and the mathematically inclined):
equals()
(javadoc) must define an equivalence relation (it must be reflexive, symmetric, and transitive). In addition, it must be consistent (if the objects are not modified, then it must keep returning the same value). Furthermore, o.equals(null)
must always return false.
hashCode()
(javadoc) must also be consistent (if the object is not modified in terms of equals()
, it must keep returning the same value).
The relation between the two methods is:
Whenever a.equals(b)
, then a.hashCode()
must be same as b.hashCode()
.
In practice:
If you override one, then you should override the other.
Use the same set of fields that you use to compute equals()
to compute hashCode()
.
Use the excellent helper classes EqualsBuilder and HashCodeBuilder from the Apache Commons Lang library. An example:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
Also remember:
When using a hash-based Collection or Map such as HashSet, LinkedHashSet, HashMap, Hashtable, or WeakHashMap, make sure that the hashCode() of the key objects that you put into the collection never changes while the object is in the collection. The bulletproof way to ensure this is to make your keys immutable, which has also other benefits.
One possible (and common) use is when you have some object that is not thread-safe, but you want to avoid synchronizing access to that object (I'm looking at you, SimpleDateFormat). Instead, give each thread its own instance of the object.
For example:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
Documentation.
Best Answer
Hibernate has a nice and long description of when / how to override
equals()
/hashCode()
in documentationThe gist of it is you only need to worry about it if your entity will be part of a
Set
or if you're going to be detaching / attaching its instances. The latter is not that common. The former is usually best handled via:equals()
/hashCode()
on a business key - e.g. a unique combination of attributes that is not going to change during object (or, at least, session) lifetime.equals()
/hashCode()
on primary key IF it's set and object identity /System.identityHashCode()
otherwise. The important part here is that you need to reload your Set after new entity has been added to it and persisted; otherwise you may end up with strange behavior (ultimately resulting in errors and / or data corruption) because your entity may be allocated to a bucket not matching its currenthashCode()
.