Fundamentals 11 min read

Understanding Java's equals() and hashCode() Methods and How to Override Them

This article explains the default implementations of Java's Object.equals() and Object.hashCode(), the required contracts for overriding them, demonstrates their behavior in the String class, and provides practical guidelines and example code for correctly overriding these methods in custom classes.

Java Captain
Java Captain
Java Captain
Understanding Java's equals() and hashCode() Methods and How to Override Them

Java's root class Object provides the methods equals() and hashCode() , which are not final and can be overridden; the article introduces two ways to use and override them while highlighting important considerations.

1. equals() Method

The default implementation simply checks reference equality:

public boolean equals(Object obj) {    return (this == obj); }

When overriding, the JDK specifies five contracts: reflexive, symmetric, transitive, consistent, and non‑null behavior.

2. hashCode() Method

2.1 Object.hashCode()

The native declaration in Object is:

public native int hashCode();

It returns a value derived from the object's memory address, ensuring distinct hash codes for different objects. Like equals() , hashCode() can be overridden, and the JDK outlines several rules:

It is used by hash‑based collections such as HashMap and HashSet .

If the fields used in equals() do not change, the hash code must remain constant.

Equal objects must have equal hash codes.

Unequal objects may share hash codes, but fewer collisions improve performance.

2.2 Role of hashCode()

When an object is added to a hash‑based collection, its hash code determines the bucket; if a collision occurs, equals() resolves whether the object is already present.

The JVM stores the hash code in the object's MarkWord, alongside other metadata.

3. equals() and hashCode() in java.lang.String

The String class overrides both methods. The relevant code is:

private final char value[]; private int hash; // Default to 0
public boolean equals(Object anObject) {
    if (this == anObject) return true;
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i]) return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

Key points:

String objects are immutable; reassignment creates a new object.

The hash code is cached for performance.

Two strings are equal if they have the same characters in the same order, regardless of reference identity.

The hash code is computed as s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1] , using the prime number 31 for a good balance of distribution and speed.

4. How to Override hashCode()

4.1 Principles

When overriding, ensure that equal objects produce equal hash codes, avoid overly simple or overly complex implementations, and keep performance in mind.

4.2 Common Algorithm (Effective Java)

Initialize a non‑zero result (e.g., int result = 17; ), then for each field used in equals() combine its hash contribution:

boolean → f ? 1 : 0

byte, char, short, int → (int) f

long → (int)(f ^ (f >>> 32))

float → Float.floatToIntBits(f)

double → Long.hashCode(Double.doubleToLongBits(f))

Object → recursive hashCode() or 0 if null

Array → use java.util.Arrays.hashCode()

Combine each field's hash with a multiplier (commonly 31).

4.3 Example

The following Person class overrides both methods, using only name and age (the fields involved in equals() ) for the hash code:

public class Person {
    private String name;
    private int age;
    private boolean gender;
    // getters and setters omitted for brevity
    @Override
    public boolean equals(Object another) {
        if (this == another) return true;
        if (another instanceof Person) {
            Person anotherPerson = (Person) another;
            return this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge();
        }
        return false;
    }
    @Override
    public int hashCode() {
        int hash = 17;
        hash = hash * 31 + getName().hashCode();
        hash = hash * 31 + getAge();
        return hash;
    }
}

This example follows the contract: objects that are equal according to equals() produce identical hash codes.

JavaStringhashingequalsHashCodeobjectoverride
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.