Sobes.tech
Middle

Why and how to override the hashCode method?

sobes.tech AI

Answer from AI

The hashCode method is used to obtain the integer hash code of an object. It is necessary for the proper functioning of collections based on hash tables, such as HashMap, HashSet, and Hashtable. When an object is added to such a collection, its hash code is used to determine the "bucket" where the object will be placed. When searching for an object, the hash code is recalculated for quick identification of the bucket to examine.

Overriding hashCode is necessary if you override the equals method. According to the contract between equals and hashCode:

  1. If two objects are equal according to the equals method, their hash codes returned by hashCode must be the same.
  2. If two objects are not equal according to the equals method, their hash codes do not have to be different, but for better collection performance, it is desirable that they are different.

If you override equals without overriding hashCode, equal objects will have different hash codes (except for random coincidences), which will lead to incorrect collection behavior:

  • Objects considered equal by equals will be placed in different buckets.
  • Searching for an equal object may fail because only one bucket determined by the hash code of the searched object will be checked.

To override hashCode, a combination of the hash codes of the object's fields involved in the equality check in equals is usually used.

Common ways to override:

  1. Using Objects.hash() (since Java 7): The simplest and recommended way. It is null-safe and uses the hash codes of the provided fields.

    // Example of using Objects.hash()
    import java.util.Objects;
    
    public class MyClass {
        private int field1;
        private String field2;
    
        // ... constructor, getters, setters
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyClass myClass = (MyClass) o;
            return field1 == myClass.field1 &&
                   Objects.equals(field2, myClass.field2);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(field1, field2); // Fields involved in equals
        }
    }
    
  2. Manual (using a simple algorithm): A lower-level approach, useful for understanding but more error-prone. Usually involves simple multiplication and addition.

    // Example of manual hashCode override
    public class MyClass {
        private int field1;
        private String field2;
    
        // ... constructor, getters, setters
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyClass myClass = (MyClass) o;
            return field1 == myClass.field1 &&
                   Objects.equals(field2, myClass.field2);
        }
    
        @Override
        public int hashCode() {
            int result = 17; // Initial value (any odd number)
            result = 31 * result + field1; // Multiply by an odd prime (31 is standard) and add the field's hash code
            result = 31 * result + (field2 != null ? field2.hashCode() : 0); // Handle null for objects
    
            return result;
        }
    }
    

The standard odd prime number (31) is used because multiplication by 31 can be optimized by the compiler (31 * i == (i << 5) - i).

  1. Using IDE: Most modern IDEs (IntelliJ IDEA, Eclipse) have features for automatically generating equals() and hashCode() methods. This is the safest and most recommended approach, as they generate code following best practices.

The main goal of overriding hashCode is to ensure that equal objects have the same hash code, which is critical for the correct operation of hash-based collections.