Java · Course navigation

Java equals, hashCode, and toString

Define when two objects are equal so that they work correctly in sets and maps.

On this page
Before you start

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

Java
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()));
  }
}
Output
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
Guided walkthroughReady
1import java.util.*;
2
3class Main {
4 static class Point {
5 private final int x;
6 private final int y;
7
8 Point(int x, int y) {
9 this.x = x;
10 this.y = y;
11 }
12
13 @Override
14 public boolean equals(Object other) {
15 if (this == other) {
16 return true;
17 }
18 if (!(other instanceof Point)) {
19 return false;
20 }
21 Point p = (Point) other;
22 return x == p.x && y == p.y;
23 }
24
25 @Override
26 public int hashCode() {
27 return Objects.hash(x, y);
28 }
29
30 @Override
31 public String toString() {
32 return "(" + x + "," + y + ")";
33 }
34 }
35
36 public static void main(String[] args) {
37 Point a = new Point(1, 2);
38 Point b = new Point(1, 2);
39 System.out.println((a == b) + " " + a.equals(b));
40
41 Set<Point> seen = new HashSet<>();
42 seen.add(a);
43 seen.add(b);
44 seen.add(new Point(3, 4));
45 System.out.println(seen.size() + " " + seen.contains(new Point(3, 4)));
46 System.out.println(a + " " + (a.hashCode() == b.hashCode()));
47 }
48}

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
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput 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.

Further reading