Java equals, hashCode, and toString
Define when two objects are equal so that they work correctly in sets and maps.
On this page
Java classes and collections.
- Define when two objects are equal so that they work correctly in sets and maps.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
By default, two objects are equal only if they are the same object. A class that represents a value, such as a point, should override equals so that two points with the same coordinates count as equal. It must then also override hashCode so that equal objects have the same hash code, otherwise hash-based collections such as HashSet and HashMap behave incorrectly.
The contract is: equal objects have equal hash codes, equals is reflexive, symmetric, and transitive, and the result does not change while the object is unchanged. Objects.equals and Objects.hash make a correct implementation short. toString should return a readable description, because it is used when an object is printed or logged.
Code example
import java.util.*;
class Main {
static class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Point)) {
return false;
}
Point p = (Point) other;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
}
public static void main(String[] args) {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
System.out.println((a == b) + " " + a.equals(b));
Set<Point> seen = new HashSet<>();
seen.add(a);
seen.add(b);
seen.add(new Point(3, 4));
System.out.println(seen.size() + " " + seen.contains(new Point(3, 4)));
System.out.println(a + " " + (a.hashCode() == b.hashCode()));
}
}false true 2 true (1,2) true
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
import java.util.*; class Main { static class Point { private final int x; private final int y; Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof Point)) { return false; } Point p = (Point) other; return x == p.x && y == p.y; } @Override public int hashCode() { return Objects.hash(x, y); } @Override public String toString() { return "(" + x + "," + y + ")"; } } public static void main(String[] args) { Point a = new Point(1, 2); Point b = new Point(1, 2); System.out.println((a == b) + " " + a.equals(b)); Set<Point> seen = new HashSet<>(); seen.add(a); seen.add(b); seen.add(new Point(3, 4)); System.out.println(seen.size() + " " + seen.contains(new Point(3, 4))); System.out.println(a + " " + (a.hashCode() == b.hashCode())); }}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| Two points are equal when their coordinates are equal, not when they are the same object. | — | — | |
| Equal points must give the same hash code. | — | — | |
| == compares references, equals compares coordinates. | — | false true | |
| b equals a and has the same hash code, so the set does not add it again. | — | false true | |
| The set holds two distinct points, and a new equal point is found. | — | false true 2 true | |
| toString gives a readable form; equal objects have equal hash codes. | — | false true 2 true (1,2) true |
Behind the scenes
How Java processes this code
The Java compiler produces bytecode. The JVM loads the class and invokes its main method.
Cost and common mistakes
equals and hashCode are O(1) here; set operations are O(1) on average.
- Overriding equals without hashCode breaks HashSet and HashMap: equal points could land in different buckets.
- Do not use mutable fields in equals and hashCode of keys. Changing a key inside a set makes it unfindable.
- The parameter of equals must be Object. A method equals(Point) is an overload, not an override.
Try it yourself
What would the set size be without the hashCode override?
Reveal answer and reasoning
Probably 3, because a and b would usually have different identity hash codes and land in different buckets, so both would be kept.