Pragmatic hashcode- and equals-method

Published Jun 07, 2017

The core of hashcode and equals is not to have a fancy algorithm, especially for hashcode. The most important thing is to get it right.

In my project I always got some negative feedback about my equals implementation. That is that it mostly looks like this:

    @Override
    public boolean equals(Object obj) {
      
      boolean equals = false;
      
      if (obj instanceof SomeObject) {
        
        SomeObject that = (SomeObject) obj;
        
        equals = this.id == that.id;
        
      }
      
      return equals;
    }

One of the major concerns is that it is not null-safe. Yeah, they were right about that. And of course they have the JAVA specification on their side. But null-safeness is something you can forget about if you follow some really simple rules: Do not pass null and do not return null. So null becomes a programming error and programming errors should not be handled in production code.

Another issue seems to be the missing object identity comparison with an early return. Here again: yes, they were right. But inreal life the performance benefit isn't worth the statement in the first place. Of course, you can insert it. But in nearly all cases you will never recognize a problem with such an equals method.

Furthermore I omitted any early return statement. That is because I do not want to have a broken control flow.

My way formulating equals in the first place is to have as less artefacts as possible but at least the necessary artefacts. Any optimizations I will introduce when the time occurs.

This kind of implementation is working in production. I never experienced NPE because of the missing null comparison. Vice versa, the other code is not obfuscated with null checks too and looks less noisy.

The initial hashcode-method will look like this:

    @Override
    public int hashCode() {
      return id;
    }

There is no fancy algorithm. And most of the time you do not need it as well. Here again: Less is more. The most important thing is that hashcode and equals meet the following contract:

If equals returns true then hashcode must return the same value. And here you see clearly the benefit. You can easily evaluate if the contract is broken. In this example equals and hashcode depend both on the field "id" which is very easy to see. The point is that I really like more to have potential transparent performance issues (which I until now never experienced) than real hard to debug errors like inaccessable objects in hash-based datastructures.

I worked on a project where serialization took place. hashcode and equals met the contract most of the time but not always. The first thing I did was to return a constant value for hashcode. Of course I got little less performance. But the function of the application was reconstructed so I could work out the real problem without stress. So following implementation is totally valid but only recommed if you have some problems with has-based datastructures:

    @Override
    public int hashCode() {
      return -1;
    }

But one thing I have to say: My focus in application development is on business applications. Maybe in high performance usecases you should implement more fancy stuff. In business applications your task is to support processes. If you are writing an application where big calculations are made or big data is processed you should not blindly follow my approach. But you should at least prefer very transparent implementations for essential functionality.

Discover and read more posts from Arne Lewinski
get started