Реализация `hashCode` для класса Point

23. Реализовать `hashCode()` для класса Point

Условие задачи:
Дан класс Point с двумя полями: int x и int y.

Необходимо:

  • реализовать метод hashCode();

  • учитывать оба поля при вычислении хеша;

  • объяснить, какое значение возвращает hashCode() и для чего оно используется.

Код:

public class Point {
    private int x;
    private int y;

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

    @Override
    public int hashCode() {
        // TODO
    }
}

Спойлеры к решению

Подсказки
💡 hashCode() возвращает целое число типа int.
💡 Если два объекта равны по equals(), их hashCode() обязан быть одинаковым.
💡 Для объединения нескольких полей часто используется множитель 31.
💡 Если equals() сравнивает x и y, эти же поля должны участвовать и в hashCode().

Решение
public class Point {
    private int x;
    private int y;

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + x;
        result = 31 * result + y;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Point)) {
            return false;
        }

        Point other = (Point) obj;

        return x == other.x
                && y == other.y;
    }
}

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

В данном случае хеш рассчитывается по обоим полям:

int result = 17;
result = 31 * result + x;
result = 31 * result + y;

Главный контракт между equals() и hashCode():

a.equals(b) == true
a.hashCode() == b.hashCode()

Обратное неверно: одинаковые хеш-коды не означают, что объекты обязательно равны. У разных объектов может возникнуть коллизия.

Например:

Point first = new Point(1, 2);
Point second = new Point(1, 2);

если equals() сравнивает координаты, то эти объекты равны и должны иметь одинаковый hashCode().

Также можно использовать более короткую запись:

@Override
public int hashCode() {
    return Objects.hash(x, y);
}

Но ручная реализация хорошо показывает принцип вычисления хеша.

Важно: если объект используется как ключ в HashMap или хранится в HashSet, поля, участвующие в equals() и hashCode(), нежелательно изменять после добавления объекта в коллекцию. Иначе объект может оказаться в неправильной хеш-корзине и перестать корректно находиться.