On overloading Object.equals() and Object.hashCode()

Unless you are new to the Java language, you already know that when you overload Object.equals(), you must also overload Object.hashCode().  You must exercise caution when overloading these two operators.  I recently ran into a problem when a simple refactoring of an object caused the system to break all due to Object.hashCode().  The resulting refactoring gave us a hash function that looked like:

public class MyObject
{
  public boolean equals(Object object)
  {
    // some code removed here for simplicity
    return ((MyObject) object).getId() == id;
  }

  public int hashCode()
  {
    return id;
  }

  private int id;
}

The object in question is persisted in a database table and the id field is mapped in the database as an automatically generated primary key column.  This guarantees the id field is unique. The problem has to do with the persistence framework changing the value of the id after the object is first stored in the database table.

The persistence framework maintains a HashSet of the objects to be inserted into a database table.  Once the object is inserted into a table, it is removed from the HashSet.  When the framework changes the id of the object, it can no longer be removed from the HashSet.  If you try the following, it will fail:

HashSet<MyObject> data = new HashSet<MyObject>();
MyObject object = new MyObject()
data.add(object);
object.setId(1);
data.remove(object);

There were two lessons learned here. First you must be careful when refactoring Object.equals() and Object.hashCode(). Prior to the refactoring, we had used a UUID to determine equality. Second, when you overload Object.equals() and Object.hashCode(), you can only use invariant values in the computation. If you (accidentally) use a value that can change, your hashed data structured will (eventually) fail.

Advertisements

2 thoughts on “On overloading Object.equals() and Object.hashCode()

  1. This is one of those type of situations that FindBugs and PMD will catch if you run them against your code. Persisting java classes is just brittle in general anyways. You can’t do a lot of structural refactoring as this will break existing classes that are already stored in the database. It’s one of the reasons I’m not a huge fan of java persistance frameworks.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s