Зачем в Java коллекциям на основе хеширования нужен метод hashCode()?

«Зачем в Java коллекциям на основе хеширования нужен метод hashCode()?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Метод hashCode() критически важен для корректной и эффективной работы хеш-коллекций (HashMap, HashSet, Hashtable). Он определяет номер «корзины» (bucket), в которую будет помещен объект, что позволяет выполнять операции get() и contains() за время, близкое к O(1).

Основные причины:

  1. Эффективный поиск: Хеш-код используется для быстрого сужения области поиска. Вместо сравнения с каждым элементом коллекции, система сначала вычисляет корзину по хеш-коду и затем сравнивает объекты внутри нее с помощью equals().
  2. Контракт с equals(): Если a.equals(b) возвращает true, то a.hashCode() обязан быть равен b.hashCode(). Нарушение этого правила приведет к тому, что объекты, которые логически равны, будут помещены в разные корзины и потеряются в коллекции.
  3. Качество распределения: Хорошая реализация hashCode() минимизирует коллизии (ситуации, когда разные объекты имеют одинаковый хеш-код), что напрямую влияет на производительность.

Пример плохой реализации (вызывает коллизии и деградацию до O(n)):

@Override
public int hashCode() {
    return 1; // Все объекты попадут в одну корзину.
}

Пример хорошей реализации (с использованием Objects.hash()):

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

    @Override
    public int hashCode() {
        // Учитывает все значимые поля, используемые в equals().
        return Objects.hash(name, age);
    }
}

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