Proper object hashCode overriding

If you have ever overridden Object.equals() without overriding Object.hashCode() read on…

The Background

In this post I am going to explain why the Object.hashCode() method must be overridden for every class that overrides Object.equals.

One of the most common sources of bugs that I have seen is developers disobeying Object.hashCode() contract. Examples on the web aren’t much better so be careful you do not fall into this pitfall. The contract as taken from the Java SE 6 spec states the following:

  1. Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode() method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  2. If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  3.     It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

Failure to obey this can have dire effects that are not so easily spotted at start. Below is a simple address.java class; with the Object.equals() method overridden correctly so that two objects are the same if the ‘flat number‘, ‘street‘ and ‘postcode‘ are the same.

Address.class

public class Address {  
  private final int flatNumber;
  private final String street;
  private final String postcode;

  public Address(int flatNumber, String street, String postcode) {
    this.flatNumber = flatNumber;
    this.street = street;
    this.postcode = postcode;
  }

  @Override
  public boolean equals(Object obj) {

    if (obj == this) {
        return true;
    }

    if (obj instanceof Address) {
        // Compare of fields relevant to composing an address.
        // If all the fields are the same then these objects are the same.
        Address address = (Address) obj;

        return address.flatNumber == flatNumber &&
                address.street.equals(street) &&
                address.postcode.equals(postcode);
    }

    return false;
  }
  
  public static void main(String[] args) {
    Address address1 = new Address(10, "Lambeth High Street", "SE1 7JN");
    Address address2 = new Address(50, "Lime Street", "EC3A 1AA");
    Address address3 = new Address(10, "Lambeth High Street", "SE1 7JN");

    if (!address1.equals(address2)) {
        System.out.println("address1 is NOT equal to address2");
    }

    if (address1.equals(address3)) {
        System.out.println("address1 is equal to address3");
    }

  }

So far so good

So far so good; nothing seems out of the ordinary. Comparing objects of the same type is returning true as expected. But the problems start when we start collecting objects in a collection.

Say for example we have the following code to collect a bunch of address and we apply the get method.

  Map<Address, String> map = new HashMap<>(); // Note the use of generics. 
  map.put(address1, "Tom"); 
  map.put(address2, "Dick"); 

  // Both address1 and address3 have the same properties; any they override the equals() method 
  // BUT because we didn't override the hashCode() they have different address in memory 

  String name = map.get(address3); 
  if (name == null) { 
    System.out.println("Why oh why is this null....."); 
  } else { 
    System.out.println("Name:" + name); 
  }

Oh didn’t expect that !

REASON: Failure to override Object.hashCode() causes two equal object to have unequal hash code; and since the set.getMethod() requires this to be true we get a ‘null‘ (or in truth an indeterministic behaviour).

Solution

Proper overriding of the Object.hashCode() method as showen below:

@Override
  public int hashCode() {
    // OK so I could have though of more inventive ways of creating a hashcode for the above class but still you get the jest of it.
    return flatNumber * (street.hashCode() + postcode.hashCode());
  }

 

Hope you enjoyed this post … any questions/corrections feel free.

3 responses

  1. Nice article; I’ve the spec but never understood the consequences of not following this contract. Would have liked more on on the use of hashcode() in collection classes to retrieve object from hashMap.

    Like

    1. Both addrress1 and address3 point to the same object in memory. Why? Two objects equals to each other does not mean they are the same object in memory, right?

      Like

      1. Indeed without implementing BOTH the equals() AND hashCode() the objects will not obtain the same memory location. I’ve updated the comment to reflect what is going on with the program.

        Like

Leave a reply to itharm Cancel reply