C++unordered map
外观
C++ unordered_map 容器详解[编辑 | 编辑源代码]
unordered_map是C++标准模板库(STL)中提供的一种关联容器,它存储的元素是键值对(key-value pair),并通过哈希表实现高效查找。与`map`不同,`unordered_map`不会自动对键进行排序,因此平均情况下能提供O(1)时间复杂度的查找性能。
基本特性[编辑 | 编辑源代码]
- 无序存储:元素不按键的顺序存储
- 唯一键:每个键只能在容器中出现一次
- 哈希实现:使用哈希表作为底层数据结构
- 快速访问:平均情况下插入、删除和查找操作都是O(1)时间复杂度
- 动态大小:容器大小会根据需要自动增长
头文件与声明[编辑 | 编辑源代码]
使用unordered_map需要包含头文件:
#include <unordered_map>
基本声明语法:
std::unordered_map<KeyType, ValueType> map_name;
构造函数[编辑 | 编辑源代码]
unordered_map提供多种构造函数:
构造函数 | 描述 |
---|---|
unordered_map() |
默认构造函数,创建空容器 |
unordered_map(size_type n) |
创建容器并预留n个桶的空间 |
unordered_map(const unordered_map& other) |
拷贝构造函数 |
unordered_map(initializer_list<value_type> il) |
使用初始化列表构造 |
示例:
// 默认构造
std::unordered_map<std::string, int> wordCount;
// 初始化列表构造
std::unordered_map<std::string, int> colors = {
{"red", 0xFF0000},
{"green", 0x00FF00},
{"blue", 0x0000FF}
};
常用成员函数[编辑 | 编辑源代码]
元素访问[编辑 | 编辑源代码]
operator[]
:访问或插入元素(键不存在时会创建)at()
:访问元素(键不存在时抛出异常)
示例:
std::unordered_map<std::string, int> ageMap = {{"Alice", 25}, {"Bob", 30}};
// 使用operator[]
ageMap["Charlie"] = 28; // 插入新元素
int aliceAge = ageMap["Alice"]; // 访问现有元素
// 使用at()
try {
int bobAge = ageMap.at("Bob");
// int unknown = ageMap.at("Unknown"); // 抛出std::out_of_range异常
} catch(const std::out_of_range& e) {
std::cerr << "Key not found: " << e.what() << '\n';
}
容量查询[编辑 | 编辑源代码]
empty()
:检查容器是否为空size()
:返回元素数量max_size()
:返回容器可容纳的最大元素数
修改器[编辑 | 编辑源代码]
insert()
:插入元素emplace()
:原位构造元素erase()
:删除元素clear()
:清空容器
示例:
std::unordered_map<int, std::string> idToName;
// 插入元素
idToName.insert({1, "Alice"});
idToName.emplace(2, "Bob"); // 效率更高
// 删除元素
idToName.erase(1); // 删除键为1的元素
// 清空容器
idToName.clear();
查找操作[编辑 | 编辑源代码]
find()
:查找指定键的元素count()
:统计具有指定键的元素数量(0或1)contains()
(C++20):检查是否包含指定键
示例:
std::unordered_map<std::string, double> priceMap = {{"apple", 1.99}, {"banana", 0.99}};
// 使用find()
auto it = priceMap.find("apple");
if (it != priceMap.end()) {
std::cout << "Price of apple: " << it->second << '\n';
}
// 使用count()
if (priceMap.count("banana") > 0) {
std::cout << "Banana exists in the map\n";
}
// C++20的contains()
#if __cplusplus >= 202002L
if (priceMap.contains("orange")) {
std::cout << "Orange exists in the map\n";
}
#endif
哈希与桶接口[编辑 | 编辑源代码]
unordered_map的性能很大程度上依赖于哈希函数和桶的数量:
桶操作[编辑 | 编辑源代码]
bucket_count()
:返回桶的数量max_bucket_count()
:返回容器能拥有的最大桶数bucket_size(n)
:返回第n个桶中的元素数bucket(key)
:返回键所在的桶编号
哈希策略[编辑 | 编辑源代码]
load_factor()
:返回负载因子(元素数/桶数)max_load_factor()
:获取或设置最大负载因子rehash(n)
:设置桶数为至少nreserve(n)
:预留至少n个元素的空间
示例:
std::unordered_map<std::string, int> myMap;
// 设置哈希策略
myMap.max_load_factor(0.7); // 设置最大负载因子
myMap.reserve(100); // 预留100个元素的空间
// 查看桶信息
std::cout << "Bucket count: " << myMap.bucket_count() << '\n';
std::cout << "Load factor: " << myMap.load_factor() << '\n';
迭代器[编辑 | 编辑源代码]
unordered_map提供前向迭代器:
std::unordered_map<std::string, int> myMap = {{"one", 1}, {"two", 2}, {"three", 3}};
// 使用迭代器遍历
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << ": " << it->second << '\n';
}
// 使用范围for循环(C++11)
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << '\n';
}
自定义哈希函数[编辑 | 编辑源代码]
对于自定义类型作为键,需要提供哈希函数:
struct Point {
int x, 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);
}
};
std::unordered_map<Point, std::string, PointHash> pointMap;
性能分析[编辑 | 编辑源代码]
unordered_map在不同操作下的时间复杂度:
操作 | 平均情况 | 最坏情况 |
---|---|---|
插入 | O(1) | O(n) |
删除 | O(1) | O(n) |
查找 | O(1) | O(n) |
实际应用案例[编辑 | 编辑源代码]
单词频率统计[编辑 | 编辑源代码]
#include <iostream>
#include <unordered_map>
#include <string>
void countWords(const std::string& text) {
std::unordered_map<std::string, size_t> wordCount;
std::string word;
for (char c : text) {
if (isalpha(c)) {
word += tolower(c);
} else if (!word.empty()) {
++wordCount[word];
word.clear();
}
}
if (!word.empty()) {
++wordCount[word];
}
// 输出结果
for (const auto& pair : wordCount) {
std::cout << pair.first << ": " << pair.second << '\n';
}
}
int main() {
std::string text = "Hello world hello c++ world";
countWords(text);
return 0;
}
输出:
hello: 2 world: 2 c: 1
缓存实现[编辑 | 编辑源代码]
#include <unordered_map>
#include <iostream>
template<typename Key, typename Value>
class SimpleCache {
private:
std::unordered_map<Key, Value> cache;
size_t maxSize;
public:
SimpleCache(size_t size) : maxSize(size) {}
bool get(const Key& key, Value& value) {
auto it = cache.find(key);
if (it != cache.end()) {
value = it->second;
return true;
}
return false;
}
void put(const Key& key, const Value& value) {
if (cache.size() >= maxSize) {
cache.erase(cache.begin()); // 简单策略:删除第一个元素
}
cache[key] = value;
}
};
int main() {
SimpleCache<std::string, int> cache(3);
cache.put("one", 1);
cache.put("two", 2);
cache.put("three", 3);
int value;
if (cache.get("two", value)) {
std::cout << "Found: " << value << '\n';
}
cache.put("four", 4); // 这会淘汰"one"
if (!cache.get("one", value)) {
std::cout << "one was evicted from cache\n";
}
return 0;
}
输出:
Found: 2 one was evicted from cache
unordered_map vs map[编辑 | 编辑源代码]
特性 | unordered_map | map |
---|---|---|
底层实现 | 哈希表 | 红黑树 |
元素顺序 | 无序 | 按键排序 |
查找时间复杂度 | 平均O(1),最坏O(n) | O(log n) |
内存使用 | 通常更高 | 通常更低 |
迭代器稳定性 | 插入/删除可能使所有迭代器失效 | 除删除元素外,迭代器保持有效 |
最佳实践[编辑 | 编辑源代码]
1. 当需要快速查找而不关心顺序时,优先使用unordered_map 2. 对于小数据集,map可能更快(哈希计算开销) 3. 对于自定义类型作为键,确保提供良好的哈希函数 4. 预分配足够空间以避免频繁重哈希 5. 考虑负载因子对性能的影响
总结[编辑 | 编辑源代码]
unordered_map是C++中高效的关联容器,特别适合需要快速查找的场景。理解其哈希实现原理和性能特性对于有效使用至关重要。通过合理选择哈希函数和调整哈希策略,可以优化其性能以满足特定应用需求。