JavaRush /Курсы /JAVA 25 SELF /Класс Objects: методы equals, hashCode, hash

Класс Objects: методы equals, hashCode, hash

JAVA 25 SELF
29 уровень , 1 лекция
Открыта

1. Знакомство с классом Objects

Переходим к теме! Если вы устали вручную проверять значения на null и писать расчёт хеша для каждого поля, вас выручит утилитный класс java.util.Objects. Его задача — сделать работу с объектами проще, лаконичнее и безопаснее.

Это действительно «швейцарский нож»: класс умеет безопасно сравнивать объекты на равенство (без риска получить NullPointerException), удобно вычислять хеш-коды, сравнивать через компаратор и проверять аргументы на null.

Objects.equals: безопасное сравнение с учётом null

Если написать просто a.equals(b), и a окажется null, прилетит NullPointerException. Ручные проверки громоздки. Objects.equals(a, b) делает всё за вас:

  • Если оба равны null — возвращает true.
  • Если ровно один равен null — возвращает false.
  • Если оба не null — вызывает обычный equals.
import java.util.Objects;

String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false

String c = null;
System.out.println(Objects.equals(a, c)); // true

String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true

Почему это удобно? Код короче, чище и защищён от случайных NPE.

2. Objects.hash и hashCode: лаконичное вычисление хеша

Переопределяя hashCode вместе с equals, легко ошибиться, особенно когда полей много. Ручной код часто выглядит громоздко и хрупко:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + age;
    return result;
}

Метод Objects.hash решает проблему — кратко, безопасно и с поддержкой null:

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    // ... конструктор, геттеры и т.д.

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Важный момент: Objects.hash использует varargs и создаёт массив — в редких высоконагруженных местах ручной hashCode может быть быстрее. Для большинства приложений разница несущественна.

3. Objects.compare: делегируем сравнение компаратору

Иногда нужно сравнить два объекта через заранее подготовленный Comparator. Вместо прямого вызова comparator.compare(a, b) можно использовать:

int result = Objects.compare(a, b, comparator);

Этот метод:

  • Возвращает 0, если объекты равны.
  • Считает null «меньше» любого не-null объекта.
  • В остальных случаях делегирует логику переданному компаратору.
import java.util.Comparator;
import java.util.Objects;

class Person {
    private String name;
    Person(String name) { 
        this.name = name; 
    }
    public String getName() { 
        return name; 
    }
}

public class Main {
    public static void main(String[] args) {
        Person a = new Person("Анна");
        Person b = new Person("Борис");
        Comparator<Person> byName = Comparator.comparing(Person::getName);

        System.out.println(Objects.compare(a, b, byName)); // <0, т.к. "Анна" < "Борис"
        System.out.println(Objects.compare(a, null, byName)); // >0, т.к. a != null
        System.out.println(Objects.compare(null, b, byName)); // <0, т.к. null < b
        System.out.println(Objects.compare(null, null, byName)); // 0
    }
}

4. Objects.requireNonNull: страховка от «невидимых» ошибок

Если метод должен принимать только не-null значения, проверяйте это сразу. Objects.requireNonNull бросит NullPointerException с вашим сообщением:

public void setName(String name) {
    this.name = Objects.requireNonNull(name, "Имя не может быть null");
}

5. Пример: корректная реализация equals, hashCode и compareTo с Objects

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = Objects.requireNonNull(name, "Имя не может быть null");
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // Сравнение по ссылке
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        // Безопасное сравнение с null
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age); // Лаконично и безопасно
    }

    @Override
    public int compareTo(Person other) {
        // Сначала сравниваем по имени, потом по возрасту
        int cmp = name.compareTo(other.name);
        if (cmp != 0) return cmp;
        return Integer.compare(age, other.age);
    }
}

Теперь можно хранить объекты в HashSet, использовать их как ключи в HashMap, сравнивать по равенству и сортировать списки (например, через Collections.sort).

6. Применение в реальных задачах: сокращение кода и уменьшение ошибок

Пример: список пользователей в приложении

Благодаря корректной паре equals/hashCode поиск в коллекциях работает предсказуемо:

import java.util.ArrayList;
import java.util.List;

List<Person> users = new ArrayList<>();
users.add(new Person("Анна", 25));
users.add(new Person("Борис", 30));

Person search = new Person("Анна", 25);
System.out.println(users.contains(search)); // true

Пример: работа с nullable-полями

Если у класса есть поля, которые могут быть null (например, отчество), используйте Objects.equals и Objects.hash:

import java.util.Objects;

public class User {
    private String firstName;
    private String middleName; // Может быть null
    private String lastName;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(firstName, user.firstName)
            && Objects.equals(middleName, user.middleName)
            && Objects.equals(lastName, user.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, middleName, lastName);
    }
}

7. Таблица: основные методы класса Objects

Метод Назначение Пример использования
Objects.equals(a, b)
Безопасное сравнение двух объектов с учётом null
Objects.equals(a, b)
Objects.hash(a, b, ...)
Лаконичное вычисление хеш-кода по нескольким полям
Objects.hash(name, age)
Objects.compare(a, b, comparator)
Сравнение через компаратор, безопасно для null
Objects.compare(p1, p2, byNameComparator)
Objects.requireNonNull(obj[, msg])
Проверка на null, выбрасывает NullPointerException
Objects.requireNonNull(name, "Имя не может быть null")
Objects.isNull(obj) / Objects.nonNull(obj)
Проверка на null/не null (удобно в Stream API)
list.stream().filter(Objects::nonNull)

8. Типичные ошибки при использовании методов класса Objects

Ошибка №1: забыли использовать Objects.equals для nullable-полей. Если сравнивать поля напрямую через equals, можно поймать NullPointerException. Используйте Objects.equals(middleName, other.middleName).

Ошибка №2: не все поля учтены в hashCode. Поля, участвующие в equals, должны участвовать и в hashCode, иначе поведение HashSet/HashMap станет непредсказуемым.

Ошибка №3: ручной hashCode с ошибкой. Соблюдать коэффициенты и проверки на null непросто. Objects.hash делает это за вас; используйте его, если нет строгих требований к производительности.

Ошибка №4: не используете Objects.requireNonNull там, где это контракт класса. Если поле не допускает null, проверяйте в конструкторе/сеттере — ошибка проявится сразу, а не «в дебрях» стека вызовов.

Ошибка №5: используете Objects.hash для массивов. Для массивов нужен Arrays.hashCode, а для вложенных массивов — Arrays.deepHashCode; аналогично для сравнения содержимого существуют Arrays.equals/Arrays.deepEquals.

1
Задача
JAVA 25 SELF, 29 уровень, 1 лекция
Недоступна
Создание цифрового отпечатка для книг в каталоге 📚
Создание цифрового отпечатка для книг в каталоге 📚
1
Задача
JAVA 25 SELF, 29 уровень, 1 лекция
Недоступна
Упорядочивание участников мероприятия по имени 🧑‍🤝‍🧑
Упорядочивание участников мероприятия по имени 🧑‍🤝‍🧑
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
kasnil Уровень 8
28 января 2026
2 утверждение для Objects.compare «• Считает null «меньше» любого не-null объекта.» не корректно. В реализации Objects.compare:

public static <T> int compare(T a, T b, Comparator<? super T> c) {
    return (a == b) ? 0 :  c.compare(a, b);
}
Т.е. если только один из параметров является null объект, то логика их сравнения передается компаратору. Т.о. для данного случая всегда будет 3 утверждение «• В остальных случаях делегирует логику переданному компаратору.» Например в данном примере, где будет всегда 1:

public class Solution {
    static void main(String[] args) {
        Person a = new Person("Анна");
        Comparator<Person> byName = (o1, o2) -> o1 == o2 ? 0 : 1;

        IO.println(Objects.compare(null, a, byName));
        IO.println(Objects.compare(a, null, byName));
    }
}

record Person(String name) { }
iseverin Уровень 66
2 февраля 2026
Отличное уточнение, но с опиской: 2 утверждение для Objects.compare«• Считает null «меньше» любого не-null объекта.» не корректно. В реализации Objects.compare:
kasnil Уровень 8
2 февраля 2026
Подправил, спасибо!