Commons Lang EqualsBuilder
and HashCodeBuilder
provide methods to automate both the equals( )
and hashCode( )
. Example
1-2 briefly demonstrates these two builders using the PoliticalCandidate
bean from the previous two
recipes.
Example 1-2. Automating hashCode( ) and equals( )
import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; // Member variables - omitted for brevity // Constructors - omitted for brevity // get/set methods - omitted for brevity // A hashCode which creates a hash from the two unique identifiers public int hashCode( ) { return new HashCodeBuilder(17, 37) .append(firstName) .append(lastName).toHashCode( ); } // An equals which compares two unique identifiers public boolean equals(Object o) { boolean equals = false; if ( o != null && PoliticalCandidate.class.isAssignableFrom(o.getClass()) ) { PoliticalCandidate pc = (PoliticalCandidate) o; equals = (new EqualsBuilder( ) .append(firstName, pc.firstName) .append(lastName, pc.lastName)).isEquals( ); } return equals; }
HashCodeBuilder
has a
constructor that takes two integer primitives. These primitives are used
as an offset when creating a hash code; both numbers should be odd,
nonzero, and prime. The HashCodeBuilder
in Example 1-2 is configured to use the firstName
and the lastName
of the PoliticalCandidate
object; therefore, two
PoliticalCandidate
objects with the
same first and last name will have identical hash codes. If a hash code
depends on every field in a class, you may use reflection to generate a
hash code:
public int hashCode( ) { return HashCodeBuilder.reflectionHashCode(this); }
Like ToStringBuilder
and
HashCodeBuilder
, the EqualsBuilder
is also configured via an
append( )
method, which takes two
arguments to compare. EqualsBuilder
's
append( )
method accepts all
primitives, objects, and arrays, and one advantage of EqualsBuilder
is the ability to compare two
arrays by simply passing them to append(
)
. When this happens, EqualsBuilder
compares every element of an
array:
int[] array1 = new int[] { 1, 3, 4, 2, 5, 3, 4, 5, 3, 4 }; int[] array2 = new int[] { 1, 3, 4, 2, 5, 3, 4, 5, 3, 4 }; int[] array3 = new int[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; EqualsBuilder builder = new EqualsBuilder( ); builder.append( array1, array2 ); boolean firstTwoEqual = builder.isEquals( ); System.out.println( "Is array1 equal to array2? " + firstTwoEqual ); EqualsBuilder builder = new EqualsBuilder( ); builder.append( array2, array3 ); boolean lastTwoEqual = builder.isEquals( ); System.out.println( "Is array2 equal to array3? " + lastTwoEqual );
The EqualsBuilder
compares the
contents of two arrays, checking to see that the corresponding element
is equal. The following output is produced:
Is array1 equal to array2? true Is array2 equal to array3? false
If two classes are equal only if every field is equal, the
EqualsBuilder
can compare two objects
using reflection as in the following code:
public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(this, o); }
Be careful when using reflection to automate hashCode()
and equals( )
, you may get more than you
bargained for. In Example 1-2, a
candidate is uniquely identified by first and last name; if this bean
were mapped to a table in a relational database, firstName
and lastName
would be a composite key
identifying each unique row. A HashMap
or HashSet
is similar to a database table in
that the identifier is defined by the fields used by hashCode()
and equals( )
; putting an equal object with the
same hash code into a HashMap
replaces the previous entry. A poorly implemented hashCode( )
or equals( )
can have unintended consequences
when storing objects in such a data structure. In other words,
equals( )
and hashCode()
should be based off of the
properties that uniquely identify a class. This being the case, the
equals()
function should return
true if two PoliticalCandidate
objects have
identical first
and last
names, and the hash code for two equal objects should be identical.
The hashCode( )
and equals()
in Example 1-2 are written to only take into
account the firstName
and lastName
properties.