Расскажи про Hash Code & Equals Contract

`hashCode()` и `equals()` играют важную роль в работе с объектами, особенно когда речь идет о коллекциях, таких как `HashSet`, `HashMap`, и `Hashtable`. Они определены в классе `Object`, и поэтому доступны для переопределения всеми классами. Правильное переопределение этих методов важно для эффективной работы коллекций, которые используют хеширование.

Контракт `hashCode()` и `equals()` - определяет, как эти методы должны взаимодействовать друг с другом:

1. Согласованность: Если два объекта равны согласно методу `equals(Object obj)`, тогда вызов `hashCode()` на каждом из объектов должен возвращать одинаковое целое значение. Это не значит, что объекты, не равные друг другу, должны возвращать различные хеш-коды. Однако, разные хеш-коды могут помочь улучшить производительность хеш-таблиц.

2. Обратное не требуется: Если `hashCode()` двух объектов возвращает одинаковое значение, это не обязательно означает, что объекты равны согласно `equals()`. Ситуация, когда разные объекты имеют одинаковые хеш-коды, называется коллизией.

Переопределение `equals(Object obj)` используется для проверки равенства двух объектов. По умолчанию, этот метод проверяет равенство ссылок, что означает, что два объекта считаются равными, только если они указывают на одно и то же место в памяти. Переопределение метода `equals()` позволяет сравнивать объекты по содержанию.

При переопределении `equals()`, убедитесь, что он:

  • Рефлексивен: для любого ненулевого ссылочного значения `x`, `x.equals(x)` должно возвращать `true`.
  • Симметричен: для любых ненулевых ссылочных значений `x` и `y`, `x.equals(y)` должно возвращать `true` тогда и только тогда, когда `y.equals(x)` возвращает `true`.
  • Транзитивен: для любых ненулевых ссылочных значений `x`, `y`, и `z`, если `x.equals(y)` возвращает `true` и `y.equals(z)` возвращает `true`, то и `x.equals(z)` должно возвращать `true`.
  • Консистентен: для любых ненулевых ссылочных значений `x` и `y`, многократные вызовы `x.equals(y)` должны последовательно возвращать `true` или последовательно возвращать `false`.
  • Для любого ненулевого ссылочного значения `x`, `x.equals(null)` должно возвращать `false`.

Переопределение `hashCode()` возвращает хеш-код объекта, который используется хеш-таблицами для определения места хранения объекта. При переопределении `equals()`, необходимо также переопределить `hashCode()`, чтобы поддерживать общий контракт для методов `hashCode()` и `equals()`.

Пример:

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
               Objects.equals(name, person.name);
    }

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

В этом примере, `equals()` сравнивает объекты по `name` и `age`, а `hashCode()` использует эти же поля для генерации хеш-кода. Это обеспечивает соблюдение контракта между `equals()` и `hashCode()`.

Правильное переопределение `equals()` и `hashCode()` критически важно для корректной работы коллекций, основанных на хеш-таблицах. Это обеспечивает эффективное распределение объектов в коллекции и корректное сравнение объектов по содержанию.

Feb. 22, 2024, easyoffer