Java hashCode and equals

java

Dec 31 2023 14:51

Joshua Bloch says on Effective Java

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.


Object equals method

Each object created by Java JVM has a generated hashCode (integer), this's also known as memory location in JVM

The default implementation of the equals method is compare object's hashCode, which means two objects with the same memory location are equally

Car carOne = new Car(1,"Model X");
Car carTwo = carOne ;
System.out.println(carOne .equals(carTwo)); // true

But from business perspective, we need to use object information to find out if objects are equally That's why we need to override the equals method

class Car{
    private int carNum;
    private String name;

    public Car(int carNum, String name) {
        this.carNum = carNum;
        this.name = name;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        HashCodeAndEqual.Car car = (HashCodeAndEqual.Car) o;
        return carNum == car.carNum && Objects.equals(name, car.name);
    }
}

The main part is return carNum == car.carNum && Objects.equals(name, car.name);

We check all properties of Car object to make sure it is equal. Sound reasonable right?

This equals method is helpful when you need to check an Object's data is changed when performing an update action (instead of comparing every single property like me in junior year :D)

Object hashCode method

Hashcode is a unique code generated by the JVM at time of object creation. It can be used to perform some operations on hashing-related algorithms like hashtable, hashmap etc. An object can also be searched with this unique code.

When do we need to override hashCode?

Let's come back to the last example, we have Car class with method equals overrides

And I want to use Car object as a key for the map

Car carOne = new Car(1, "Model X");
Car carTwo = new Car(1, "Model X");

System.out.println(carOne .equals(carTwo )); // true

Map<Car, Integer> carMap = new HashMap<>();
carMap.put(carOne , 1);
carMap.put(carTwo , 2);

System.out.println(carMap.size()); // return 2

When choosing Map, I expect it helps me to replace the duplicate key when put() method call

The map should only contain one item because carOne and carTwo are equally, but size() returns 2

First of all, HashMap checks if the hashCode of carTwo is the same as carOne. Only if the values are the same, it will proceed to check the equality in the same bucket.

But here the hashCode is different for these 2 objects (because they have different memory address-from default implementation). Hence it will not even care to check for equality.

So it's the main reason we need to override the hashCode, let's add this override method to make Map keep one item only

The method generates hashCode based on object information (carNum and name), so the object with the same information will have the same hashCode, then HashMap will pass hashcode check step and continue with the equals() check

@Override
public int hashCode() {
    return Objects.hash(carNum, name);
}

Conclusion

  • Hashcode is a unique code generated by the JVM at the time of object creation
  • Default equals() method use hashCode to compare objects
  • Override equals() method when you want to compare objects based on object property
  • Override hashCode when you work with Hash-based Collections
  • Two objects may have the same hashcode but not the same memory address
me

Pham Duc Minh

Da Nang, Vietnam