Know how to implement hashcode and equals

Summary

Implementing hashCode and equals is not straight-forward. Do not implement them unless it is necessary to do so. If you do implement them make sure you know what you are doing.

Details

It is well known that if you override equals you must also override the hashCode method (see Effective Java item 9).

If logically equal objects do not have the same hashCode they will behave in a surprising manner if placed in a hash based collection such as HashMap.

By surprising we mean make your program behave incorrectly in a fashion that it very difficult to debug.

Unfortunately implementing equals is surprisingly hard to do correctly. Effective Java item 8 spends about 12 pages discussing the topic.

The contract for equals is handily stated in the Javadoc of java.lang.Object. We will not repeat it here or repeat the discussion of what it means that can be found in Effective Java and large swathes of the internet, instead we will look at strategies for implementing it.

Which ever strategy you adopt it is important that you first write tests for your implementation.

It is easy for an equals method to cause hard to diagnose bugs if the code changes (e.g if a fields are added or their type changes). Writing tests for equals methods used to be a painful and time-consuming procedure, but libraries now exist that make it trivial to specify the common cases (see Testing FAQs).

Don't

This is the simplest strategy and the one you should adopt by default in the interests of keeping your codebase small.

Most classes do not need an equals method. Unless your class represents some sort of value it makes little sense to compare it with another so stick with the inherited implementation from Object.

An irritating grey area are value classes where the production code never has a requirement to compare equality but the test code does. The dilemma here is whether to implement the methods purely for the benefit of the tests or to complicate the test code with custom equality checks.

There is of course no right answer here, but we would suggest first trying the compare-it-in-the test approach before falling back to providing a custom equals method.

The custom equality checks can be cleanly shared by implementing a custom assertion using a library such as AssertJ or Hamcrest.

Effective Java tentatively suggests having your class throw an error if equals is unexpectedly called

@Override public boolean equals(Object o) {
  throw new AssertionError(); // Method is never called
}

This seems like a good idea, but unfortunately will confuse most static analysis tools. On balance it probably creates more problems than it solves.

Auto generate with an IDE

Most IDEs provide some method of auto-generating hashCode and equals methods. This is an easily accessible method of generating hashCode and equals, but the resulting methods are (depending on the IDE and its settings) often ugly and complex such as the ones generated by Eclipse shown below.

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((field1 == null) ? 0 : field1.hashCode());
    result = prime * result + ((field2 == null) ? 0 : field2.hashCode());
    return result;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
   MyClass  other = (MyClass) obj;
    if (field1 == null) {
      if (other.field1 != null)
        return false;
    } else if (!field1.equals(other.field1))
      return false;
    if (field2 == null) {
      if (other.field2 != null)
        return false;
    } else if (!field2.equals(other.field2))
      return false;
    return true;
  }

Unless your IDE can be configured to produce clean methods (as discussed below) we do not generally recommend this approach as it is easy for bugs to be introduced into this code by hand editing over time.

Hand roll clean methods

Java 7 introduced the java.util.Objects class that makes implementing hashCode trivial. Guava provides the similar com.google.common.base.Objects class which may be used with earlier versions of Java.

  @Override
  public int hashCode() {
    return Objects.hash(field1, field2);
  }

The Objects class also simplifies implementing equals a little by pushing most null checks into the Objects.equals method.

  @Override
  public boolean equals(Object obj) {
    if (this == obj) // <- performance optimisation
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass()) // <- see note on inheritance
      return false;
    MyClass other = (MyClass) obj;
    return Objects.equals(field1, other.field1) &&
        Objects.equals(field2, other.field2);
  }

The first if statement is not logically required and could be safely omitted, it may however provide performance benefits.

Usually we would recommend that such micro-optimisations are not included unless they are been proven to provide a benefit, but in the case of equals methods we suggest that the optimisation is left in place. It is likely to justify itself in at least some of your classes and there is value in having all methods follow an identical template.

The example above uses getClass to check that objects are of the same type. An alternative is to use instanceof as follows

  @Override
  public boolean equals(Object obj) {
    if (this == obj) 
      return true;
    if (obj == null)
      return false;
    if (!(obj instanceof MyClass)) // <- compare with instanceof 
      return false;
    MyClass other = (MyClass) obj;
    return Objects.equals(field1, other.field1) &&
        Objects.equals(field2, other.field2);
  }

This results in a behavioural difference - comparing instances of MyClass with its subclasses will return true with instanceof but false with getClass.

In Effective Java Josh Bloch argues in favour of instanceof as the getClass implementation violates a strict interpretation of the Liskov substitution principle.

However if instanceof is used it is easy for the symmetric property of the equals contract to be violated if a subclass overrides equals. i.e

MyClass a = new MyClass();
ExtendsMyClassWithCustomEqual b = new ExtendsMyClassWithCustomEqual();

a.equals(b) // true
b.equals(a) // false, a violation of symmetry

If you find yourself in a situation where you need to consider the nuances of whether subclasses are equal to their parents we strongly suggest you reconsider your design.

Having to think about maintaining the equals contract in a class hierarchy is painful and you shouldn't need to put yourself or your team through this for normal server side coding tasks.

In the majority of cases if you think it makes sense for your class to implement hashCode and equals we strongly suggest you make your class final so hierarchies do not need to be considered.

If you believe you have a case where it makes sense for subclasses to be treated as equivalent to their parent use instanceof, but ensure that the parent equals method is made final.

Avoid relationships that are more complex than this.

Commons EqualsBuilder and HashCodeBuilder

The apache commons hashcode and equals builders were once a popular way of generating these methods. We do not recommend their use in new code as most of what they achieved is now provided by java.util.Objects without bringing in a 3rd party library, or by the Guava equivalent.

These classes do provide the option of a single line reflection based implementation.

public boolean equals(Object obj) {
  return EqualsBuilder.reflectionEquals(this, obj);
}
public int hashCode() {
  return HashCodeBuilder.reflectionHashCode(this);
}

The brevity of these implementations is attractive, but their performance is measurably poor compared to all the implementations discussed so far. If you are confident that you will pick up real performance bottlenecks with testing and profiling then using these as initial placeholder implementations may be a reasonable approach, but in general we suggest you avoid them.

Lombok

Lombok allows you to annotate your class so that hashCode and equals methods are generated at build time. To be able to work with code using Lombok you will also require support for the annotations in your IDE.

Although auto-generating boilerplate methods is on the face of it a very sensible approach, most teams we have spoken to that have adopted Lombok and similar technologies have eventually removed them due to the friction caused by the tooling and the confusion caused by the not-quite-java magic.