Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Как правильно реализовать equals и hashCode

Короткий ответ

Если два объекта равны по equals(), у них должен быть одинаковый hashCode(). Поэтому эти методы всегда переопределяют вместе:

class Point {
    final int x;
    final int y;
 
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
 
    @Override
    public boolean equals(Object o) {
        return o instanceof Point other && x == other.x && y == other.y;
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}
 
void main() {
    IO.println(new Point(1, 2).equals(new Point(1, 2)));
    IO.println(new HashSet<>(List.of(new Point(1, 2))).contains(new Point(1, 2)));
}

Результат:

true
true

Проще — запись

Запись генерирует оба метода по всем полям:

record Point(int x, int y) {}
 
void main() {
    IO.println(new Point(1, 2).equals(new Point(1, 2)));
    IO.println(new Point(1, 2).hashCode() == new Point(1, 2).hashCode());
}

Результат:

true
true

Сравнение по части полей

Иногда равенство определяет только идентификатор. Используйте одни и те же поля в обоих методах:

class User {
    final long id;
    String name;
 
    User(long id, String name) {
        this.id = id;
        this.name = name;
    }
 
    @Override
    public boolean equals(Object o) {
        return o instanceof User other && id == other.id;
    }
 
    @Override
    public int hashCode() {
        return Long.hashCode(id);
    }
}
 
void main() {
    IO.println(new User(7, "Anna").equals(new User(7, "Anna Smith")));
}

Результат:

true

Строки и null

Для полей-объектов, которые могут быть null, используйте Objects.equals():

class Contact {
    final String email;
 
    Contact(String email) {
        this.email = email;
    }
 
    @Override
    public boolean equals(Object o) {
        return o instanceof Contact other && Objects.equals(email, other.email);
    }
 
    @Override
    public int hashCode() {
        return Objects.hashCode(email);
    }
}
 
void main() {
    IO.println(new Contact(null).equals(new Contact(null)));
}

Результат:

true

Частая ошибка

Переопределить только equals(). Объекты равны, но HashSet их не находит, потому что ищет по разным хеш-кодам:

class Point {
    final int x;
 
    Point(int x) {
        this.x = x;
    }
 
    @Override
    public boolean equals(Object o) {
        return o instanceof Point other && x == other.x;
    }
}
 
void main() {
    var set = new HashSet<Point>();
    set.add(new Point(1));
 
    IO.println(new Point(1).equals(new Point(1)));
    IO.println(set.contains(new Point(1)));
}

Результат:

true
false

Функции из этого ответа

  • Object.equals() — Сравнивает объекты по смыслу. По умолчанию работает как ==, поэтому в своих классах его переопределяют.
  • Object.hashCode() — Возвращает хеш-код объекта — число, по которому HashMap и HashSet быстро находят объект.
  • Objects.hash() — Вычисляет хеш-код из нескольких значений — для реализации hashCode().
  • Objects.equals() — Сравнивает два объекта через equals(), безопасно для null.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.