1.6. Automating hashCode( ) and equals( )

Problem

You need a way to automatically implement equals() and hashCode( ) methods.

Solution

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.HashCodeBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;

public class PoliticalCandidate {

    // 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) ) {
            PoliticalCandidate pc = (PoliticalCandidate) o;
            equals = (new EqualsBuilder( )
                       .append(firstName, ps.firstName)
                       .append(lastName, ps.lastName)).isEquals( );
        }
        return equals;
    }
}

Discussion

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);
}

Warning

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.

Get Jakarta Commons Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.