Author: Manish Hatwalne
The Java super class java.lang.Object
has two very important methods defined in it. They
are -
public boolean equals(Object obj)
public int hashCode()
These methods prove very important when user classes are confronted with other Java classes, when objects of
such classes are added to collections etc. These two methods have become part of Sun Certified Java Programmer
1.4 exam (SCJP 1.4) objectives. This article intends to provide the necessary information about these two methods
that would help the SCJP 1.4 exam aspirants. Moreover, this article hopes to help you understand the mechanism
and general contracts of these two methods; irrespective of whether you are interested in taking the SCJP 1.4 exam
or not. This article should help you while implementing these two methods in your own classes.
public boolean equals(Object obj)
This method checks if some other object passed to it as an argument is equal to the object on which this
method is invoked. The default implementation of this method in Object
class simply checks if two
object references x and y refer to the same object. i.e. It checks if x == y
. This particular comparison
is also known as "shallow comparison". However, the classes providing their own implementations of the
equals
method are supposed to perform a "deep comparison"; by actually comparing the relevant
data members. Since Object
class has no data members that define its state, it simply performs shallow
comparison.
This is what the JDK 1.4 API documentation says about the equals
method of Object
class-
Indicates whether some other object is "equal to" this one.
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any reference values x and y, this method returns true if
and only if x and y refer to the same object (x==y has the value true).
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.
The contract of the equals
method precisely states what it requires. Once you understand it completely,
implementation becomes relatively easy, moreover it would be correct. Let's understand what each of this really
means.
equals
method to behave otherwise.
equals
method that has comparison
with an object of java.lang.String
class, or with any other built-in Java class for that matter. It
is very important to understand this requirement properly, because it is quite likely that a naive implementation
of equals
method may violate this requirement which would result in undesired consequences.
equals
method in
such a way that it provides comparison for objects of class A and class B. Now, if author of class B decides to
modify its equals
method such that it would also provide equality comparison with class C; he would
be violating the transitivity principle. Because, no proper equals
comparison mechanism would exist
for class A and class C objects.
null
, hence
the equals
method must return false if a null
is passed to it as an argument.
You have to ensure that your implementation of the equals
method returns false
if a null
is passed to it as an argument.
The details about these two methods are interrelated and how they should be overridden correctly is discussed
later in this article.
public int hashCode()
This method returns the hash code value for the object on which this method is invoked. This method returns
the hash code value as an integer and is supported for the benefit of hashing based collection classes such as
Hashtable, HashMap, HashSet etc. This method must be overridden in every class that overrides the equals
method.
This is what the JDK 1.4 API documentation says about the hashCode
method of Object
class-
Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.
hashCode
is:
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.)
As compared to the general contract specified by the equals
method, the contract specified by the
hashCode
method is relatively simple and easy to understand. It simply states two important requirements
that must be met while implementing the hashCode
method. The third point of the contract, in fact
is the elaboration of the second point. Let's understand what this contract really means.
hashCode
method must be consistently the same for multiple invocations during the same execution of the application as long
as the object is not modified to affect the equals
method.
hashCode
counterpart of the requirement specified by the equals
method. It simply emphasizes the same relationship
- equal objects must produce the same hash code. However, the third point elaborates that unequal objects need
not produce distinct hash codes.
After reviewing the general contracts of these two methods, it is clear that the relationship between these two methods can be summed up in the following statement -
Equal objects must produce the same hash code as long as they are equal, however unequal objects need not produce distinct hash codes.
The rest of the requirements specified in the contracts of these two methods are specific to those methods and
are not directly related to the relationship between these two methods. Those specific requirements are discussed
earlier. This relationship also enforces that whenever you override the equals
method, you must override
the hashCode
method as well. Failing to comply with this requirement usually results in undetermined,
undesired behavior of the class when confronted with Java collection classes or any other Java classes.
The following code exemplifies how all the requirements of equals
and hashCode
methods
should be fulfilled so that the class behaves correctly and consistently with other Java classes. This class implements
the equals
method in such a way that it only provides equality comparison for the objects of the same
class, similar to built-in Java classes like String and other wrapper classes.
1. public class Test 2. { 3. private int num; 4. private String data; 5. 6. public boolean equals(Object obj) 7. { 8. if(this == obj) 9. return true; 10. if((obj == null) || (obj.getClass() != this.getClass())) 11. return false; 12. // object must be Test at this point 13. Test test = (Test)obj; 14. return num == test.num && 15. (data == test.data || (data != null && data.equals(test.data))); 16. } 17. 18. public int hashCode() 19. { 20. int hash = 7; 21. hash = 31 * hash + num; 22. hash = 31 * hash + (null == data ? 0 : data.hashCode()); 23. return hash; 24. } 25. 26. // other methods 27. }
Now, let's examine why this implementation is the correct implementation. The class Test has two member variables
- num
and data
. These two variables define state of the object and they also participate
in the equals
comparison for the objects of this class. Hence, they should also be involved in calculating
the hash codes of this class objects.
Consider the equals
method first. We can see that at line 8, the passed object reference is compared
with this
object itself, this approach usually saves time if both the object references are referring
to the same object on the heap and if the equals comparison is expensive. Next, the if
condition at
line 10 first checks if the argument is null
, if not, then (due to the short-circuit nature of the
OR ||
operator) it checks if the argument is of type Test
by comparing the classes of
the argument and this object. This is done by invoking the getClass()
method on both the references.
If either of these conditions fails, then false
is returned. This is done by the following code -
if((obj == null) || (obj.getClass() != this.getClass()))
return false; // prefer
This conditional check should be preferred instead of the conditional check given by -
if(!(obj instanceof Test)) return false; // avoid
This is because, the first condition (code in blue) ensures that it will return false
if the argument
is a subclass of the class Test
. However, in case of the second condition (code in red) it fails.
The instanceof
operator condition fails to return false
if the argument is a subclass
of the class Test
. Thus, it might violate the symmetry requirement of the contract. The instanceof
check is correct only if the class is final
, so that no subclass would exist. The first condition
will work for both, final and non-final classes. Note that, both these conditions will return false
if the argument is null
. The instanceof
operator returns false
if the left
hand side (LHS) operand is null
, irrespective of the operand on the right hand side (RHS) as specified
by JLS
15.20.2. However, the first condition should be preferred for better type checking.
This class implements the equals
method in such a way that it provides equals comparison only for
the objects of the same class. Note that, this is not mandatory. But, if a class decides to provide equals comparison
for other class objects, then the other class (or classes) must also agree to provide the same for this class so
as to fulfill the symmetry and reflexivity requirements of the contract. This particular equals
method
implementation does not violate both these requirements. The lines 14 and 15 actually perform the equality comparison
for the data members, and return true if they are equal. Line 15 also ensures that invoking the equals
method on String variable data
will not result in a NullPointerException
.
While implementing the equals
method, primitives can be compared directly with an equality operator
(==
) after performing any necessary conversions (Such as float to Float.floatToIntBits
or double to Double.doubleToLongBits
). Whereas, object references can be compared by invoking their
equals
method recursively. You also need to ensure that invoking the equals
method on
these object references does not result in a NullPointerException
.
Here are some useful guidelines for implementing the equals
method correctly.
==
operator to check if the argument is the reference to this object, if yes.
return true. This saves time when actual comparison is costly.
null
and it is of the correct type,
if not then return false
.if((obj == null) || (obj.getClass() != this.getClass())) return false;
ClassCastException
.
==
)
after performing any necessary conversions (Such as float to Float.floatToIntBits
or double to Double.doubleToLongBits
).
Whereas, object references can be compared by invoking their equals
method recursively. You also need
to ensure that invoking equals
method on these object references does not result in a NullPointerException
,
as shown in the example above (Line 15).equals
method. Only you can decide which class members are significant and which are not.
equals
method. It takes a java.lang.Object
as an argument, do not use your own class instead. If you do that, you will not be overriding the equals
method, but you will be overloading it instead; which would cause problems. It is a very common mistake, and since
it does not result in a compile time error, it becomes quite difficult to figure out why the code is not working
properly.
equals
method to verify that it fulfills all the requirements stated by the general
contract of the equals
method.
hashCode
method whenever you override the equals
method, that's unpardonable. ;)
Now, let's examine the hashCode
method of this example. At line 20, a non-zero constant value 7
(arbitrary) is assigned to an int variable hash
. Since the class members/variables num
and data
do participate in the equals
method comparison, they should also be involved
in the calculation of the hash code. Though, this is not mandatory. You can use subset of the variables that participate
in the equals
method comparison to improve performance of the hashCode
method. Performance
of the hashCode
method indeed is very important. But, you have to be very careful while selecting
the subset. The subset should include those variables which are most likely to have the greatest diversity of the
values. Sometimes, using all the variables that participate in the equals
method comparison for calculating
the hash code makes more sense.
This class uses both the variables for computing the hash code. Lines 21 and 22 calculate the hash code values
based on these two variables. Line 22 also ensures that invoking hashCode
method on the variable data
does not result in a NullPointerException
if data
is null
. This implementation
ensures that the general contract of the hashCode
method is not violated. This implementation will
return consistent hash code values for different invocations and will also ensure that equal objects will have
equal hash codes.
While implementing the hashCode
method, primitives can be used directly in the calculation of the
hash code value after performing any necessary conversions, such as float to Float.floatToIntBits
or double to Double.doubleToLongBits
. Since return type of the hashCode
method is int
,
long values must to be converted to the integer values. As for hash codes of the object references, they should
be calculated by invoking their hashCode
method recursively. You also need to ensure that invoking
the hashCode
method on these object references does not result in a NullPointerException
.
Writing a very good implementation of the hashCode
method which calculates hash code values such that
the distribution is uniform is not a trivial task and may require inputs from mathematicians and theoretical computer
scientist. Nevertheless, it is possible to write a decent and correct implementation by following few simple rules.
Here are some useful guidelines for implementing the hashCode
method correctly.
int
variable, called hash
.
int var_code
for each variable var
as follows -
(var)
is byte, char, short
or int
, then var_code
= (int)var;
(var)
is long
, then var_code = (int)(var ^ (var >>>
32));
(var)
is float
, then var_code = Float.floatToIntBits(var);
(var)
is double
, then -long bits = Double.doubleToLongBits(var);
var_code = (int)(bits ^ (bits >>> 32));
(var)
is boolean
, then var_code = var ? 1 : 0;
(var)
is an object reference, then check if it is null
, if yes then
var_code = 0;
otherwise invoke the hashCode
method recursively on this object reference
to get the hash code. This can be simplified and given as -var_code = (null == var ? 0 : var.hashCode());
var_code
in the original hash code hash
as follows - hash = 31 * hash + var_code;
hash
.
hashCode
method and check if it is returning equal hash codes for equal objects.
Also, verify that the hash codes returned for the object are consistently the same for multiple invocations during
the same execution.
The guidelines provided here for implementing equals
and hashCode
methods are merely
useful as guidelines, these are not absolute laws or rules. Nevertheless, following them while implementing these
two methods will certainly give you correct and consistent results.
equals
method provides "deep comparison" by checking if two objects are logically
equal as opposed to the "shallow comparison" provided by the equality operator ==
.
equals
method in java.lang.Object
class only provides "shallow
comparison", same as provided by the equality operator ==
.
equals
method only takes Java objects as an argument, and not primitives; passing primitives
will result in a compile time error.
equals
method will never result in a compile time error
or runtime error.
java.lang.String
, if the equals
argument
type (class) is different from the type of the object on which the equals
method is invoked, it will
return false.
java.lang.StringBuffer
does not override the equals
method, and hence it
inherits the implementation from java.lang.Object
class.
equals
method must not provide equality comparison with any built in Java class, as it would
result in the violation of the symmetry requirement stated in the general contract of the equals
method.
null
is passed as an argument to the equals
method, it will return false.
return 1;
is a legal implementation of the hashCode
method, however it is a very
bad implementation. It is legal because it ensures that equal objects will have equal hash codes, it also ensures
that the hash code returned will be consistent for multiple invocations during the same execution. Thus, it does
not violate the general contract of the hashCode
method. It is a bad implementation because it returns
same hash code for all the objects. This explanation applies to all implementations of the hashCode
method which return same constant integer value for all the objects.
java.lang.Short, java.lang.Byte, java.lang.Character
and java.lang.Integer
simply return the value they represent as the hash code by typecasting it to
an int
.
java.lang.String
caches its hash code, i.e. it calculates the
hash code only once and stores it in an instance variable and returns this value whenever the hashCode
method is called. It is legal because java.lang.String
represents an immutable string.
Here is a list of few more resources that might be useful if you are interested in knowing more about these two methods, their implementation and significance.
java.lang.Object
class. The general contract of these two methods is available
here.
pdf
format.
This chapter deals with all the methods of java.lang.Object
class. It also discusses in details the
implementation of equals
and hashCode
methods, correct and incorrect way of overriding
them etc. This article is partially based on the information given in this book.equals
and
hashCode
- This FAQ question discusses the importance of overriding equals
and hashCode
methods correctly. It also discusses important issues regarding these two methods with respect to Java collection
framework.
This article has been reviewed and revised repeatedly so as to make it as correct as possible. Thanks a
lot to Valentin Crettaz and friends at JavaRanch for suggesting
many changes. Especially thanks to Valentin for pointing out the violation of the symmetry requirement by using
the instanceof
condition in the equals
method. If you notice any ambiguity, error in
the article and/or the mock test, please do let me know. Your feedback is very important in making this article
and test more useful.