std::unordered_map в C++: хеш-таблица для быстрого поиска с примерами
std::unordered_map в C++ — это контейнер из стандартной библиотеки, который хранит данные в формате «ключ-значение» и обеспечивает очень быстрый доступ по ключу. Если говорить проще, это хеш-таблица: вы передаёте ключ, а контейнер почти мгновенно находит связанное с ним значение.
Поисковый запрос, под который полезна эта статья: «unordered_map C++ примеры». Разберём синтаксис, основные операции, практические задачи и ошибки, которые часто допускают начинающие.
Когда нужен std::unordered_map
Используйте std::unordered_map, когда вам нужно быстро:
- найти значение по уникальному ключу;
- посчитать количество одинаковых элементов;
- хранить настройки, кэш, словарь, таблицу пользователей;
- проверять, встречался ли объект раньше.
Главная особенность: элементы внутри unordered_map не отсортированы. Если вам нужен обход ключей в порядке возрастания, лучше использовать std::map. Если порядок не важен, чаще всего unordered_map быстрее.
Подключение и базовый пример
Для работы нужен заголовочный файл <unordered_map>. Рассмотрим простой словарь возрастов:
#include <iostream>
#include <unordered_map>
#include <string>
int main() {
std::unordered_map<std::string, int> ages;
ages["Anna"] = 25;
ages["Ivan"] = 30;
ages["Oleg"] = 22;
std::cout << "Возраст Ivan: " << ages["Ivan"] << '\n';
return 0;
}
Тип std::unordered_map<std::string, int> означает: ключ — строка, значение — целое число.
Добавление элементов: operator[] и insert
Самый простой способ добавить или изменить элемент — квадратные скобки:
std::unordered_map<std::string, int> scores;
scores["Alice"] = 100; // добавление
scores["Alice"] = 150; // изменение значения
Но у operator[] есть важная особенность: если ключа нет, он будет создан автоматически со значением по умолчанию. Для int это будет 0, для std::string — пустая строка.
Если вы не хотите случайно создавать элемент, используйте insert или emplace:
std::unordered_map<std::string, int> scores;
scores.insert({"Bob", 80});
scores.emplace("Kate", 95);
emplace часто предпочтительнее, потому что создаёт объект прямо внутри контейнера без лишнего копирования.
Проверка наличия ключа
Типичная ошибка новичков — проверять наличие ключа через operator[]:
if (scores["Tom"] == 0) {
// Плохо: если ключа Tom не было, он только что появился в map
}
Правильный способ — использовать find:
auto it = scores.find("Tom");
if (it != scores.end()) {
std::cout << "Tom найден, баллы: " << it->second << '\n';
} else {
std::cout << "Tom не найден\n";
}
Начиная с C++20 можно использовать более читаемый метод contains:
if (scores.contains("Tom")) {
std::cout << "Ключ существует\n";
}
Практический пример: подсчёт частоты слов
Одна из самых популярных задач для std::unordered_map — посчитать, сколько раз встречается каждый элемент. Например, частоту слов:
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
int main() {
std::vector<std::string> words = {
"cpp", "java", "cpp", "python", "cpp", "java"
};
std::unordered_map<std::string, int> frequency;
for (const auto& word : words) {
++frequency[word];
}
for (const auto& pair : frequency) {
std::cout << pair.first << ": " << pair.second << '\n';
}
return 0;
}
Здесь выражение ++frequency[word] работает удобно: если слова ещё нет, оно создаётся со значением 0, затем увеличивается до 1.
Обход элементов
Обходить unordered_map можно через range-based for. Но помните: порядок вывода не гарантирован.
for (const auto& [name, age] : ages) {
std::cout << name << " - " << age << '\n';
}
Синтаксис [name, age] называется structured bindings и доступен с C++17. Если вы изучаете современный C++ и хотите уверенно разбираться не только в контейнерах, но и в базовых принципах языка, посмотрите курс «C++ с нуля до уверенного уровня: практика, задачи и понятные объяснения».
Удаление элементов
Удалить элемент можно по ключу:
ages.erase("Oleg");
Метод erase возвращает количество удалённых элементов. Для unordered_map это будет либо 0, либо 1, потому что ключи уникальны.
if (ages.erase("Oleg") == 1) {
std::cout << "Элемент удалён\n";
} else {
std::cout << "Такого ключа не было\n";
}
at() против operator[]
Для чтения значений можно использовать at(). В отличие от квадратных скобок, он не создаёт новый элемент. Если ключа нет, будет выброшено исключение std::out_of_range.
try {
std::cout << ages.at("Maria") << '\n';
} catch (const std::out_of_range&) {
std::cout << "Ключ Maria не найден\n";
}
Практическое правило: для добавления и счётчиков удобен operator[], для безопасного чтения — find, contains или at.
Производительность и reserve
В среднем операции поиска, вставки и удаления в std::unordered_map выполняются за O(1). Но при большом количестве вставок контейнер может перераспределять внутренние корзины. Если вы заранее знаете примерное число элементов, используйте reserve:
std::unordered_map<std::string, int> users;
users.reserve(10000);
Это может заметно ускорить программу, если вы загружаете много данных, например из файла или базы.
Ключи пользовательского типа
Для стандартных типов вроде int, std::string, long long хеш уже определён. Но если ключ — ваша структура, нужно указать, как сравнивать объекты и как вычислять хеш.
#include <iostream>
#include <unordered_map>
struct Point {
int x;
int y;
bool operator==(const Point& other) const {
return x == other.x && y == other.y;
}
};
struct PointHash {
std::size_t operator()(const Point& p) const {
return std::hash<int>{}(p.x) ^ (std::hash<int>{}(p.y) << 1);
}
};
int main() {
std::unordered_map<Point, std::string, PointHash> points;
points[{10, 20}] = "Дом";
points[{5, 7}] = "Магазин";
std::cout << points[{10, 20}] << '\n';
}
Для новичков это уже более продвинутая тема, но важно знать: unordered_map может работать не только со строками и числами.
Частые ошибки
- Ожидать отсортированный порядок. В
unordered_mapпорядок обхода непредсказуем. - Использовать operator[] только для проверки. Так можно случайно добавить новый элемент.
- Забывать про reserve. При массовой вставке предварительное резервирование ускоряет работу.
- Выбирать unordered_map всегда. Если нужны отсортированные ключи или диапазонные запросы, берите
std::map.
Краткая шпаргалка
std::unordered_map<std::string, int> m;
m["one"] = 1; // добавить или изменить
m.emplace("two", 2); // добавить эффективнее
if (m.find("one") != m.end()) {
// ключ найден
}
m.erase("two"); // удалить по ключу
std::cout << m.size(); // количество элементов
Вывод
std::unordered_map в C++ — отличный контейнер для быстрого доступа по ключу. Он особенно полезен для словарей, подсчёта частот, кэшей и проверки уникальности. Главное — помнить, что элементы не сортируются, а operator[] может создавать новые записи. Если эти особенности учитывать, хеш-таблица станет одним из самых удобных инструментов в вашем C++-коде.
-
Создано 15.07.2026 17:01:11
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.