The behavior of hashCode and equals methods
Sometimes, you need to compare objects of your custom class between each other. The java.lang.Object
class, which is the superclass for any class, provides two methods to do that: equals(Object obj)
and hashCode()
. Their default behavior is the following:
boolean equals(Object obj)
checks whether this and some other object are stored in the same memory address;int hashCode()
returns an integer hash code that is unique for each object (object's identity).
Let's look at how they behave on the simple Person
class with three fields.
class Person {
private String firstName;
private String lastName;
private int age;
// constructor, getters and setters
}
There are two objects that actually represent the same person (i.e., the objects are logically equivalent):
Person p1 = new Person("John", "Smith", 31);
Person p2 = new Person("John", "Smith", 31);
But the equals
method considers them to be different persons since it compares references rather than the values of their fields:
System.out.println(p1.equals(p2)); // false
The hashCode
method also says nothing about their equality:
System.out.println(p1.hashCode()); // 242131142
System.out.println(p2.hashCode()); // 1782113663
equals(Object obj)
and hashCode()
is not enough to compare objects of a custom class by the values of their fields.equals
behave for the standard String
class (and for many other standard classes).String person1 = new String("John Smith");
String person2 = new String("John Smith");
System.out.println(person1.equals(person2)); // true
System.out.println(person1.hashCode()); // 2314539
System.out.println(person2.hashCode()); // 2314539
Person
class, we should override both methods. It is not enough to just override the equals(Object obj)
method without hashCode()
.Overriding equals
To have the logical equality of objects, we should override the equals
method of the class. It is not as trivial as it may sound.
There are some math restrictions placed on the behavior of equals
, which are listed in the documentation for Object
.
- Reflexivity: for any non-null reference value
x
,x.equals(x)
should returntrue
. - Symmetry: for any non-null reference values
x
andy
,x.equals(y)
should returntrue
if and only ify.equals(x)
returnstrue
. - Transitivity: for any non-null reference values
x
,y
, andz
, ifx.equals(y)
returnstrue
andy.equals(z)
returnstrue
, thenx.equals(z)
should returntrue
. - Consistency: for any non-null reference values
x
andy
, multiple invocations ofx.equals(y)
consistently returntrue
or consistently returnfalse
, provided that no information used inequals
comparisons on the objects is modified. - Non-nullity: for any non-null reference value
x
,x.equals(null)
should returnfalse
.
To create a method that satisfies the listed restrictions, first, select some fields to use them in comparison and then perform three tests inside the equals
method:
- if this and other object have the same reference, the objects are equal, otherwise - go to step 2;
- if the other object is
null
or has an unsuitable type, the objects are not equal, otherwise - go to step 3; - if all selected fields are equal, the objects are equal, otherwise, they are not equal.
equals
method will work properly in some cases.Person
that overloads the equals
method. It uses all three fields in comparison.class Person {
private String firstName;
private String lastName;
private int age;
// constructor, getters and setters
@Override
public boolean equals(Object other) {
/* Check this and other refer to the same object */
if (this == other) {
return true;
}
/* Check other is Person and not null */
if (!(other instanceof Person)) {
return false;
}
Person person = (Person) other;
/* Compare all required fields */
return age == person.age &&
Objects.equals(firstName, person.firstName) &&
Objects.equals(lastName, person.lastName);
}
}
In the example above, we use java.util.Objects.equals(obj1, obj2)
to check string fields are equal. This approach avoids NPE.
Person p1 = new Person("John", "Smith", 31); // a person
Person p2 = new Person("John", "Smith", 31); // the same person
Person p3 = new Person("Marry", "Smith", 30); // another person
System.out.println(p1.equals(p2)); // true
System.out.println(p2.equals(p3)); // false
System.out.println(p3.equals(p3)); // true (reflexivity)
As you can see, now the equals
method compares two objects and returns true
if they are equal, otherwise - false
.
Overriding hashCode
If you override equals
, the good practice is to override hashCode
as well. Otherwise, your class cannot be used correctly in any collection that applies hashing mechanism (such as HashMap
, HashSet
or HashTable
).
Below we give the general contract which consists of thee conditions (taken from the doc).
1) Whenever it is invoked on the same object more than once during an execution of a Java application, thehashCode
method must consistently return the same integer, provided no information used in equals
comparisons on the object is modified. This integer doesn't need to not remain consistent from one execution of an application to another execution of the same application.person1.hashCode(); // 400000 - ok
person1.hashCode(); // 400000 - ok
person1.hashCode(); // 500000 - not ok
equals(Object)
method, then calling the hashCode
method on each of the two objects must produce the same integer result.person1.equals(person2); // true
person1.hashCode() == person2.hashCode(); // false - not ok, it must be true
person1.equals(person3); // false
person1.hashCode() == person3.hashCode(); // true - will work
The simplest implementation of the hashCode()
method may look as follows:
@Override
public int hashCode() {
return 42;
}
hashCode
method, we recommend the algorithm proposed by Joshua Bloch in the book Effective Java.Create a
int result
and assign a non-zero value (i.e.17
).For every field
f
tested in theequals()
method, calculate a hash codecode
by:- Calculate the hash code for
f
(theint
type):
- If the field
f
is aboolean
: calculate(f ? 0 : 1)
; - If the field
f
is abyte
,char
,short
orint
: calculate(int) f
; - If the field
f
is along
: calculate(int)(f ^ (f >>> 32))
; - If the field
f
is afloat
: calculateFloat.floatToIntBits(f)
; - If the field
f
is adouble
: calculateDouble.doubleToLongBits(f)
and handle the return value like every long value; - If the field
f
is an object: use the result of thehashCode()
method or 0 iff == null
; - If the field
f
is an array: see every field as separate element and calculate the hash value in a recursive fashion and combine the values as described next.
code
with result
as follows: result = 31 * result + code;
.result
as hash code of the object.Here we apply the described algorithm to the Person
class.
class Person {
private String firstName;
private String lastName;
private int age;
// constructor, getters and setters
// overridden equals method
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (firstName == null ? 0 : firstName.hashCode());
result = 31 * result + (lastName == null ? 0 : lastName.hashCode());
result = 31 * result + age;
return result;
}
}
hashCode()
for three objects. Two of the objects represent the same person.Person p1 = new Person("John", "Smith", 31); // a person
Person p2 = new Person("John", "Smith", 31); // the same person
Person p3 = new Person("Marry", "Smith", 30); // another person
System.out.println(p1.hashCode()); // 409937238
System.out.println(p2.hashCode()); // 409937238
System.out.println(p3.hashCode()); // 689793455
So, we have the same hash code for equal objects.
Note, since Java 7, we got an java.util.Objects.hash(Object... values)
utility method for hashing:
Objects.hash(firstName, secondName, age);
Summary
The default behavior of the equals
method provided by the java.lang.Object
class checks whether objects references are equal. This is not enough if you would like to compare objects by the values of their fields. In this case, you should override the equals
method in your class.
The correct implementation should satisfy the following restrictions: reflexivity, symmetry, transitivity, consistency, and non-nullity. You also should override the hashCode
method, taking into account that:
- if two objects are equal, they MUST also have the same hash code;
- if two objects have the same hash code, they do NOT have to be equal too.
hashCode()
and equals()
methods, we do not recommend to implement them manually in industrial programming. Modern IDEs such as IntelliJ IDEA or Eclipse can generate correct implementations for both methods automatically. This approach will help you to avoid bugs since overriding these methods is quite error-prone.If you'd like to know more, read the book: Joshua Bloch. Effective Java. You can also read this article as an addition to the topic.