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'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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.