Apache Commons EqualsBuilder and HashCodeBuilder

I previously blogged on the Apache Commons ToStringBuilder and discussed how it takes away much of the tedium normally associated with implementing toString methods. While implementing toString() does provide significant value in debugging and logging and is a recommended practice in Joshua Bloch's Effective Java (Item 10 in Second Edition), it normally does not impact the logic and performance of an application (unless toString() is specifically used as part of the logic). However, there are methods defined in Object that do impact both logic and performance in an application and two of them [equals() and hashCode()] are discussed in this blog entry.

While hashCode() and equals() typically impact logic and performance more than toString() does, they are also often more tricky to implement correctly. Many Java developers follow the Joshua Bloch's advice for implementing these methods as described in Effective Java (where 18 pages of the core 315 pages are devoted to these two methods). For example, the article Hashtables: When You Create Your Own Key Object in a Hashtable, Be Careful summarizes the rules that an equals() method should adhere to and provides Bloch's recommendations in Java code. The article Hashing it Out: Designing hashCode() and equals() Effectively and Correctly also discusses how to implement these two important methods (equals and hashCode). Of course, the easiest rule to remember is that when one of these two methods is overridden, the other one should be as well.

Because it can be tricky to implement hashCode() and equals() correctly, it is helpful to have reusable implementations of these provided as part of Apache Commons Lang builder package (same package that contains the previously mentioned ToStringBuilder). Even better, these implementations were explicitly written to follow Bloch's frequently cited advice as described in the Javadoc documentation for both EqualsBuilder and HashCodeBuilder.

In my blog entry on ToStringBuilder, I demonstrated and heavily used its reflective capabilities. I am less inclined to use reflection capabilities in conjunction with EqualsBuilder and HashCodeBuilder because these methods are often used in situations where performance is a major issue. Details on applying reflection-based use of EqualsBuilder and HashCodeBuilder are available here and in the respective Javadoc descriptions for these classes.

The example code used in this blog entry is very simple and only scratches the surface of what EqualsBuilder and HashCodeBuilder are capable of accomplishing. However, the example code does provide a simple example of common use of these two classes. An added bonus is that the code also demonstrates the Commons CLI and Commons Lang ToStringBuilder in action as well.

The first class to look at is the SimpleDataExample class because it is the class that actually contains the implementations of equals() and hashCode() methods using EqualsBuilder and HashCodeBuilder respectively. This example also uses ToStringBuilder to implement its toString() method.

package dustin.builders;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

/**
 * This is a "simple" data class intended for demonstration of Apache Commons
 * EqualsBuilder and HashCodeBuilder.  This is an immutable class and all of its
 * state must be provided at construction.
 * 
 * @author Dustin
 */
public class SimpleDataExample
{
   /** ID associated with this class. */
   private final Long id;

   /** Name of data (does not need to be unique). */
   private final String name;

   /**
    * Constructor accepting arguments to populate my state.
    * 
    * @param newId ID of this object instance.
    * @param newName Name of this object instance.
    */
   public SimpleDataExample(
      final Long newId, final String newName)
   {
      this.id = newId;
      this.name = newName;
   }

   /** Private constructor - not meant to be used. */
   private SimpleDataExample()
   {
      this.id = null;
      this.name = null;
   }

   /**
    * Provide my ID.
    * 
    * @return My ID.
    */
   public Long getId()
   {
      return this.id;
   }

   /**
    * Provide my name.
    * 
    * @return My name.
    */
   public String getName()
   {
      return this.name;
   }

   /**
    * My hash code implementation.
    * 
    * @return My hash code.
    */
   @Override
   public int hashCode()
   {
      return new HashCodeBuilder()
         .append(this.id)
         .append(this.name)
         .toHashCode();
   }

   /**
    * My implementation of equals() method.  The NetBeans-generated version is
    * left in place (but commented out) to notice the order of magnitude of code
    * required without EqualsBuilder.
    * 
    * @param obj The object to compare to me for equality.
    * @return true if the other object and I are equal; false otherwise.
    */
   @Override
   public boolean equals(Object obj)
   {
      if (obj instanceof SimpleDataExample == false)
      {
        return false;
      }
      if (this == obj)
      {
         return true;
      }
      final SimpleDataExample otherObject = (SimpleDataExample) obj;

      return new EqualsBuilder()
         .append(this.id, otherObject.id)
         .append(this.name, otherObject.name)
         .isEquals();
/*
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final SimpleDataExample other = (SimpleDataExample) obj;
      if (this.id != other.id && (this.id == null || !this.id.equals(other.id)))
      {
         return false;
      }
      if (this.name != other.name && (this.name == null || !this.name.equals(other.name)))
      {
         return false;
      }
      return true;
 */
   }

   /**
    * Provide String representation of me.
    * 
    * @return String representation of me.
    */
   @Override
   public String toString()
   {
      return new ToStringBuilder(this)
         .append("ID", this.id)
         .append("Name", this.name)
         .toString();
   }
}

The code of most interest from this blog entry's perspective is all in the class above, particularly in the equals() and hashCode() methods. The next code listing lists a "test" class that uses the simple data class defined above in both an ArrayList and a HashSet, depending on the command-line argument provided to its main() method. This demonstrates Commons CLI, but more importantly demonstrates use of the equals() and hashCode() methods in the data class.

1 2 Page 1
Page 1 of 2