← Назад к вопросам

Что такое коллизия в HashMap?

1.0 Junior🔥 131 комментариев
#STL контейнеры и алгоритмы#Структуры данных и алгоритмы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое коллизия в HashMap?

Коллизия (hash collision) — это ситуация, когда две разные ключи имеют одинаковый хеш-код. Так как количество возможных ключей бесконечно, а ячеек в таблице конечное число, коллизии неизбежны. Вопрос только в том, как их правильно обрабатывать.

Пример коллизии

std::unordered_map<std::string, int> map;
map.size() = 16;  // Размер таблицы

// Хеш-функция просто берёт остаток от количества ячеек
структура данных:
int hash("apple") = 123 % 16 = 11
int hash("orange") = 456 % 16 = 8
int hash("banana") = 235 % 16 = 11  // КОЛЛИЗИЯ! Как "apple"

Двое ключей ("apple" и "banana") хотят занять позицию 11.

Методы разрешения коллизий

1. Chaining (цепочки, привязка)

На каждой позиции хранится список (linked list) всех элементов с одинаковым хешем:

Index   Value
  0     []
  1     []
  ...
  8     [orange]
  ...
  11    [apple] -> [banana]  // Цепочка из 2 элементов
  ...
  15    []

Поиск: hash(key) → перейти в ячейку → пройти по цепочке, сравнивая ключи.

std::unordered_map в C++ использует именно этот метод.

std::unordered_map<std::string, int> map;
map["apple"] = 1;
map["banana"] = 2;
// Внутри: оба элемента в позиции 11, связаны в цепочку

int value = map["apple"];  // O(1) в среднем, O(n) в худшем

2. Open Addressing (открытая адресация)

Если позиция занята, ищем другую свободную ячейку по алгоритму зондирования:

Linear Probing:

Если позиция i занята, попробуем i+1, i+2, i+3...

Вставка "banana" при коллизии с "apple" на позиции 11:
  11: [apple] -> занято
  12: [] -> свободно!
  banana -> позиция 12

Quadratic Probing:

Пробуем позиции: i, i+1, i+4, i+9, i+16... (i+k²)
Уменьшает clustering (скопление элементов подряд)

Double Hashing:

Вторая хеш-функция определяет шаг: i, i+h₂(key), i+2*h₂(key)...
Расстояние между попытками зависит от ключа

Сравнение методов

МетодВставкаПоискПамятьКешевые попадания
ChainingO(1) сред.O(1) сред.+30%Плохие
Linear ProbingO(1) сред.O(1) сред.МинимаХорошие
QuadraticO(1) сред.O(1) сред.Мин.Средние
Double HashO(1) сред.O(1) сред.Мин.Хорошие

Коэффициент загрузки (load factor)

load_factor = количество элементов / размер таблицы

// Хороший диапазон: 0.5 - 0.75
// При превышении обычно выполняется rehashing

Когда load factor > 0.75, HashMap часто автоматически удваивает размер:

std::unordered_map<std::string, int> map;
map.max_load_factor(0.8);  // Установить свой порог

Плохая хеш-функция — проблема

// Плохая функция (много коллизий)
int bad_hash(int key) {
    return key % 16;  // Если ключи: 16, 32, 48, 64... - все коллизии!
}

// Хорошая функция (распределение)
int good_hash(int key) {
    return (key * 2654435761U) >> 27;  // Множитель Fibonacci
}

Для C++ разработчика

std::unordered_map/set использует chaining по умолчанию. Для критичных по производительности мест можно:

// Резервировать место заранее
std::unordered_map<int, int> map;
map.reserve(10000);  // Избежать rehashing

// Пользовательская хеш-функция
struct MyHash {
    size_t operator()(int key) const {
        return std::hash<int>{}(key ^ 0x9e3779b9);
    }
};
std::unordered_map<int, int, MyHash> map;

Совет: Всегда профилируйте! Иногда std::map (красно-чёрное дерево, O(log n)) работает быстрее, чем HashMap с плохой хеш-функцией.