Как правильно реализовать 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.