Как вычисляется метод hashCode() в Java и на основании каких данных?

Ответ

Метод hashCode() возвращает целочисленное значение (хэш-код), вычисляемое на основе внутреннего состояния объекта. Его цель — эффективная работа с хэш-коллекциями (HashMap, HashSet).

Стандартные реализации:

  • Примитивные обертки (Integer, Long): хэш-код равен значению.
  • String: вычисляется по полиномиальной формуле на основе всех символов строки.
  • Массивы: Arrays.hashCode() вычисляет хэш на основе элементов.
  • Объекты по умолчанию: Object.hashCode() часто возвращает преобразованный адрес в памяти.

Правила и контракт с equals():

  1. Если a.equals(b) == true, то a.hashCode() == b.hashCode() обязательно.
  2. Обратное не обязательно: одинаковые хэш-коды не гарантируют равенство объектов (коллизия).

Пример корректного переопределения hashCode() и equals():

public class Person {
    private final String name;
    private final 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() {
        // Используем вспомогательный метод Objects.hash()
        return Objects.hash(name, age);
    }
}

Важно: Для изменяемых (mutable) полей, используемых в вычислении хэша, изменение поля после добавления объекта в HashSet или как ключа в HashMap приведет к невозможности его найти.

Ответ 18+ 🔞

Давай разберёмся с этим вашим hashCode(), а то народ путается, как последние идиоты. Ну, типа, представь, что у тебя есть объект, и ты хочешь его быстро находить в какой-нибудь коллекции, а не перебирать всё подряд, как конченый.

Что это вообще такое, этот хэш-код? Это, блядь, просто число. Целое число. Вычисляется оно из того, что внутри объекта — из полей, грубо говоря. Главная задача — чтобы хэш-таблицы (эти твои HashMap, HashSet) работали быстро, а не ебались как кони с поиском.

Как его обычно получают, этот самый код?

  • С примитивами (Integer, Long) — вообще зашибись: хэш-код равен самому значению. Integer a = 42; — хэш тоже 42. Проще некуда.
  • Со строками (String) — там уже умнее, формула такая полиномиальная, по всем символам бежит. Поэтому "пизда" и "пизда" дадут одинаковый хэш, а "пизда" и "писда" — уже разные, ёпта.
  • С массивами — тут надо Arrays.hashCode() вызывать, он за тебя всё посчитает на основе элементов. Самому ебаться не надо.
  • А если ты свою херню написал (класс свой) и hashCode() не переопределил — получишь дефолтную реализацию от Object. Она часто просто адрес в памяти как-то преобразует. И тогда даже два одинаковых, по твоей логике, объекта будут иметь разные хэши. И в HashMap ты их не найдешь, будешь сидеть и ебать мозг, что за хуйня.

А теперь, сука, главное правило — контракт с equals()! Запоминай раз и навсегда, чтобы потом не выть:

  1. Если a.equals(b) вернул true (объекты равны по твоей бизнес-логике), то обязательно должно быть a.hashCode() == b.hashCode(). Это железно. Нарушишь — HashMap и HashSet начнут глючить, как пьяный слесарь.
  2. Обратное НЕ обязательно! Если хэши совпали — это ещё не значит, что объекты равны. Это называется коллизия, бывает. Хэш-таблицы с этим умеют жить.

Смотри, как это обычно делают, не выёбываясь:

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // один и тот же объект, чё проверять-то
        if (o == null || getClass() != o.getClass()) return false; // не null и того же класса
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name); // сравниваем поля
    }

    @Override
    public int hashCode() {
        // Идеально — использовать готовый `Objects.hash()`. Он сам там всё посчитает.
        return Objects.hash(name, age);
    }
}

И последнее, охуенно важное предупреждение! Если ты используешь для хэша изменяемые поля (не final), и меняешь их после того, как засунул объект в HashSet или как ключ в HashMap — ты обречён. Объект ты уже не найдешь, потому что он будет искаться по старому хэшу, в другой корзине. Получится, что объект как бы есть, но его в упор не видно. Пиздец и карачун. Поэтому либо делай поля неизменяемыми, либо не используй их в hashCode(), либо будь готов к сюрпризам, хитрая жопа.