equals() and hashCode()

exercise No. 216

Q:

Consider the following Person class:

public class Person {

    private String name, // Guaranteed never
            comment;     // to be null

    private  int ageInYears;

    // Other methods omitted for brevity

    @Override public boolean equals(Object o) {
        if (o instanceof  Person) {
            final Person p = (Person) o;
            return ageInYears == p.ageInYears &&
                    name.equals(p.name);
        } else {
            return false;
        }
    }

    @Override public int hashCode() {
        return ageInYears + 13 * comment.hashCode();
    }
}

We assume the above equals() implementation to be correct. Answer the following two questions:

  1. Is this implementation correct regarding the contract between equals() and hashCode()? Correct if necessary.

  2. After correcting possible flaws: Is the resulting implementation »good« with respect to distinguishing objects? Amend if possible.

Explain your answers.

A:

  1. The equals(...) method defines two Person instances to be equal when having common age and Name. Thus two instances e.g. (18, "Eve Porter", "New Friend from holiday") and (18, "Eve Porter", "Friendly person")of common name and age but having a different comment will still considered to be equal.

    Since equality is being defined solely on age and name the comment attribute must not be used for implementing hashCode(). Otherwise the above example will result in different hash values contradicting the equals(...) / hashCode() contract. We thus have:

    public class Person {
      ...
        @Override public int hashCode() {
            return ageInYears;
        }
    }
  2. The previously corrected hashCode() implementation is correct with respect to its related equals() method. However all Person instances of common age but different name will be defined to be different by our equals(...) method. But they will all share the same hashCode() value throughout. So our hashCode() method is not particularly good when it comes to distinguish objects. Adding the name property to our hashCode() calculation solves this issue:

    public class Person {
      ...
        @Override public int hashCode() {
            return ageInYears + 13 * name.hashCode();
        }
    }