PassFront
  1. PassFront
  2. База знаний
  3. Стеки, очереди и хеш-таблицы

Как спроектировать хеш-ключ для составного объекта и почему важны equals/hashCode?

Стеки, очереди и хеш-таблицы

Хеш-таблица находит элемент по двум вопросам — «в какой корзине?» (hashCode) и «это тот же ключ?» (equals); если равные по смыслу объекты дают разные хеши или ключ мутирует после вставки, запись становится недоступной.

Контракт equals/hashCode

  • Равные по equals объекты обязаны иметь равный hashCode; обратное не требуется.
  • Переопределили equals, но не hashCode в Java — map.get по «равному» ключу вернёт null.
  • Ключ должен быть неизменяемым: после мутации хеш другой, поиск идёт не в ту корзину, а запись висит в таблице мёртвым грузом.

JS Map и объектные ключи

В JS нельзя переопределить равенство: Map сравнивает объекты по ссылке, поэтому map.get({ x: 1, y: 2 }) вернёт undefined. Варианты:

const grid = new Map(); // ключ "x:y" → число
const key = (x, y) => `${x}:${y}`;
grid.set(key(1, 2), 42);
grid.get(key(1, 2)); // 42

// Без разделителя ["1", "12"] и ["11", "2"] дали бы один ключ "112"
const tupleKey = (...parts) => JSON.stringify(parts);
  • Строковая сериализация: разделитель обязателен, а JSON.stringify объекта зависит от порядка свойств — сериализуйте массив полей в фиксированном порядке.
  • Упаковка чисел: x * WIDTH + y для ограниченных целых — быстрее строк.
  • Вложенные Map<x, Map<y, V>> — без сериализации вообще.
  • Интернирование: фабрика возвращает один и тот же объект для равных полей.

Коротко для собеседования

Таблица ищет корзину по hashCode, а сверяет ключ по equals, поэтому равные объекты обязаны давать одинаковый хеш, а ключ не должен меняться после вставки. В JS Map сравнивает объекты по ссылке, так что составной ключ сериализуют в строку с разделителем или упаковывают в число, а не прогоняют через JSON.stringify целиком.

Другие вопросы раздела