











unordered_map 底层是哈希表,采用链地址法解决哈希冲突,C++标准只规定接口和复杂度,不强制底层细节,主流实现(GCC libstdc++):桶数组 + 单向链表。
头文件:
#include <unordered_map>存储单元:pair<const Key, T>,key 不可修改
bucket array(桶数组,vector实现)
├─ bucket0 → 节点1 → 节点2 → nullptr
├─ bucket1 → 节点3 → nullptr
├─ bucket2 → nullptr
└─ bucket3 → 节点4 → 节点5 → nullptr
pair<const Key, T>,以及链表下一个节点指针。hash<Key>(key),得到哈希值;== 判断key是否已存在:
end()。重点:哈希只用来定位桶,判定key相等必须用 ==。哈希相同 ≠ key相等(哈希冲突)。
template<
class Key,
class T,
class Hash = std::hash<Key>, // 哈希函数对象
class KeyEqual = std::equal_to<Key>,// key相等判断
class Allocator = std::allocator<std::pair<const Key, T>>
> class unordered_map;
规则:如果两个key相等,则它们的哈希值必须相等,否则哈希表逻辑错误。
内置类型如 int、std::string 标准库已经提供 std::hash;自定义结构体当key,需要自己提供hash函数和==运算符。
$$load_factor = \frac{元素总数}{桶的数量}$$
max_load_factor():最大负载因子,默认 1.0load_factor > max_load_factor,触发 rehash⚠️ rehash 会全部迭代器失效;但元素本身的指针/引用依然有效,节点只是换桶。
reserve(n):预分配桶,预留空间,避免中途多次rehash。
unordered_map:桶内永远是单向链表,不会转红黑树!
这点和 Java HashMap 不一样(Java 链表长度超过阈值转为红黑树),C++标准没有这个要求。
| unordered_map | map | |
|---|---|---|
| 底层 | 哈希表(链地址) | 红黑树 |
| 有序 | 无序 | key升序有序 |
| 复杂度 | 平均O(1),最坏O(n) | O(logn) |
| 迭代器失效 | rehash全部失效;删除仅被删迭代器失效 | 插入几乎不失效;删除仅被删迭代器失效 |
| 自定义key | 需要hash函数 + == | 需要 < 比较运算符 |
const,不能修改key,修改会破坏哈希表;reserve;#include <vector>
#include <list>
#include <functional>
#include <utility>
template<typename K, typename V>
class MyHashmap {
private:
std::vector<std::list<std::pair<K,V>>> buckets;
size_t bucket_num;
size_t elem_cnt;
public:
MyHashmap(size_t n = 10) : bucket_num(n), elem_cnt(0), buckets(n) {}
size_t get_bucket(const K& key) {
return std::hash<K>{}(key) % bucket_num;
}
void insert(const K& key, const V& val) {
size_t idx = get_bucket(key);
auto& bucket = buckets[idx];
for(auto& p : bucket) {
if(p.first == key) {
p.second = val;
return;
}
}
bucket.emplace_back(key, val);
elem_cnt++;
// 负载因子超过阈值,rehash
if((double)elem_cnt / bucket_num > 1.0) {
rehash();
}
}
void rehash() {
auto old_buckets = std::move(buckets);
bucket_num *= 2;
buckets.resize(bucket_num);
elem_cnt = 0;
for(auto& list : old_buckets) {
for(auto& p : list) {
insert(p.first, p.second);
}
}
}
};
参考资料:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。