Прямое сравнение объектов в Java почти всегда (кроме строк) будет возвращать false
для разных объектов, даже если их содержимое идентично:
var u1 = new User("Mike");
var u2 = new User("Anna");
u1 == u2; // false
Иногда необходимо другое поведение, например, сравнение на основе каких-то значений. Пример с городами:
var city1 = new City("London");
var city2 = new City("London");
// А мы хотим true
city1 == city2; // false
equals()
Эта задача тесно связана с классом Object
. Именно в нем реализован механизм, который мы используем для сравнения строк.
str1.equals(str2);
В случае строк equals()
сравнивает сами строки. Для всех остальных типов объектов, стандартная реализация возвращает true
только в том случае, если объект сравнивается сам с собой. Это поведение идентично сравнению через ==
.
var city1 = new City("London");
var city2 = new City("London");
// По умолчанию поведение equals и == одинаково
// для всех объектов кроме строк
city1 == city2; // false
city1.equals(city2); // false
Но это поведение можно изменить так как нам нужно, благодаря возможности переопределить этот метод в нужном классе. Предположим что у нас есть класс User
с полями firstName
и age
. Его реализация:
public class User {
private String firstName;
private int age;
public User(String firstName, int age) {
this.firstName = firstName;
this.age = age;
}
// Геттеры и сеттеры
}
Допустим мы хотим сравнивать пользователей на основе равенства их firstName
и возраста. Если имя и возраст одинаковые, то мы считаем, что это тот же самый пользователь. Реализовать эту проверку можно напрямую:
var u1 = new User("Nika", 22);
var u2 = new User("David", 22);
var u3 = new User("Nika", 22);
u1.getFirstName().equals(u2.getFirstName()) &&
u1.getAge() == u2.getAge()); // false
u1.getFirstName().equals(u3.getFirstName()) &&
u1.getAge() == u3.getAge()); // true
Но нам бы хотелось сравнивать через метод equals
, что значительно упрощает процесс, особенно если проверок по коду много.
u1.equals(u2); // false
// Чтобы так заработало, нужно переопределить equals
// на сравнение firstName
u1.equals(u3); // true
Чтобы иметь возможность сравнивать пользователей на основе их firstName
и возраста, нам понадобится реализовать метод equals()
с таким содержимым:
class User {
// Должен работать для любых объектов, которые передаются во внутрь
// поэтому тип входного параметра Object
@Override
public boolean equals(Object obj) {
// Если объект сравнивается сам с собой
if (this == obj) {
return true;
}
// Проверяем что объект того же класса
if (obj == null || getClass() != obj.getClass()) {
return false;
}
// Приводим тип к нужному, в данном случае User
User person = (User) obj;
// Сравниваем нужные поля
return firstName.equals(person.firstName) && age == person.age;
}
// Остальной код класса
}
Большая часть этого кода идентична для всех классов. Разница появляется только в самом конце, где мы выбираем по каким полям происходит сравнение и то как это делается.
hashCode()
Переопределение equals()
при сравнении объектов, важное условие работы этой системы, но недостаточное. Этот метод работает в связке с методом hashCode()
, который нужно переопределять тогда, когда переопределяется equals()
. Почему? hashCode()
возвращает числовой хеш-код, используемый в коллекциях для хранения объектов. Этот код должен быть одинаковым у объектов, считающихся одинаковыми с точки зрения метода equals()
. Подробнее мы с этим столкнемся тогда, когда начнем изучать коллекции.
Для класса User
, объекты которого сравниваются на основе firstName
и age
его реализация может быть такой:
@Override
public int hashCode() {
return Objects.hash(firstName, age);
}
Статический метод Objects.hash()
возвращает уникальный (с оговорками) числовой код для всех переданных в него параметров. То есть ровно то, что требуется от метода hashCode()
Вывод
Сравнение объектов в Java реализуется с помощью методов equals()
и hashCode()
. Основные правила при работе с equals()
и hashCode()
звучат так:
- Если переопределяется
equals()
, то должен переопределятьсяhashCode()
. - Одинаковые объекты должны возвращать одинаковый хеш-код.
- Разные объекты могут возвращать одинаковый хеш-код.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.