std::unorderd_map 简介

1. unorderd_map 简介

简介

  • unordered_map 是 C++ 标准库中的一个容器,它定义在 <unordered_map> 头文件里。它借助哈希表来存储键值对,能快速查找、插入和删除元素
  • 快速查找:平均情况下,查找、插入和删除操作的时间复杂度为 O ( 1 ) O(1) O(1)。不过在最坏情况下,例如哈希冲突严重时,时间复杂度会达到 O ( n ) O(n) O(n)
  • 无序存储:unordered_map 不会按照键的顺序存储元素,元素的存储顺序由哈希函数决定。
  • 键的唯一性:每个键在 unordered_map 中是唯一的。若尝试插入已存在的键,新的值会覆盖旧的值。

1.1. 实现原理

  • 底层实现主要基于哈希表(Hash Table)
  1. 哈希函数
  • unordered_map 使用哈希函数将键转换为一个无符号整数,这个整数将作为数组的索引。
  • 对于基本数据类型(如 int、string 等),标准库已经提供了默认的哈希函数。
  • 但对于自定义类型,需要用户自己定义哈希函数。

例如,对于自定义类型 MyKey,可以这样定义哈希函数:

#include <functional>

struct MyKey {
   
   
    int a;
    double b;
    // 重载相等运算符,用于比较键是否相等
    bool operator==(const MyKey& other) const {
   
   
        return a == other.a && b == other.b;
    }
};

<think>我们被要求比较std::unordered_mapstd::map(通常基于红黑树实现)。两者都是关联容器,但有不同的特性和性能特征。 我们将从以下几个方面进行对比: 1. 内部实现 2. 时间复杂度 3. 元素顺序 4. 内存使用 5. 迭代器有效性 6. 使用场景 然后给出代码示例说明基本操作和注意事项。 </think>### `std::unordered_map` vs `std::map` 全面对比 在 C++ 中,`std::unordered_map` 和 `std::map` 都是关联容器,但在实现、性能特性和使用场景上有显著差异: --- #### 1. **底层实现** | 容器 | 实现原理 | 结构特点 | |--------------------|--------------------------|--------------------------| | `std::map` | **红黑树**(自平衡二叉搜索树) | 节点存储键值对 + 左右子节点指针 | | `std::unordered_map` | **哈希表**(桶数组 + 链表/红黑树) | 数组存储桶头指针 + 链表存储冲突元素 | ```cpp // map: 树状结构示意 struct TreeNode { std::pair<Key, Value> data; TreeNode* left; TreeNode* right; // ... 平衡信息 }; // unordered_map: 哈希桶结构示意 std::vector<Bucket> buckets; // 桶数组 struct Node { std::pair<Key, Value> data; Node* next; // 冲突链表 }; ``` --- #### 2. **时间复杂度对比** | 操作 | `std::map` | `std::unordered_map` | |-------------------|-----------------|----------------------| | 插入 (`insert`) | O(log n) | **O(1)** 平均 / O(n) 最坏 | | 查找 (`find`) | O(log n) | **O(1)** 平均 / O(n) 最坏 | | 删除 (`erase`) | O(log n) | **O(1)** 平均 / O(n) 最坏 | | 范围遍历 | **O(n)** | O(n) 但缓存不友好 | | 有序遍历 | **有序迭代** | 无序 | --- #### 3. **元素顺序特性** | 特性 | `std::map` | `std::unordered_map` | |-----------------------|--------------------------------|--------------------------------| | 键排序 | 严格按**键升序**(默认`<`比较) | **无任何顺序保证** | | 迭代顺序 | 稳定的中序遍历顺序 | 依赖哈希函数和桶布局 | | 自定义排序 | 支持自定义比较器 | 不支持 | ```cpp // map 保证有序输出 std::map<int, std::string> m{{3, "C"}, {1, "A"}, {2, "B"}}; for (auto& [k, v] : m) std::cout << k; // 输出: 1 2 3 // unordered_map 无序输出 std::unordered_map<int, std::string> um{{3, "C"}, {1, "A"}, {2, "B"}}; for (auto& [k, v] : um) std::cout << k; // 可能输出: 2 1 3 或其他顺序 ``` --- #### 4. **内存与性能考量** | 因素 | `std::map` | `std::unordered_map` | |---------------------|------------------------------|-------------------------------| | 内存开销 | 每个节点额外 2 指针 + 平衡信息 | 桶数组 + 每个元素链表指针 | | 缓存友好度 | 较差(节点分散) | **更差**(哈希冲突时跳跃访问) | | 哈希函数要求 | 不需要 | 需要高质量哈希函数 (`std::hash`) | | 自定义哈希支持 | 不支持 | 支持自定义哈希器 | --- #### 5. **迭代器有效性** | 操作 | `std::map` | `std::unordered_map` | |---------------------|----------------------------------|----------------------------------| | 插入元素 | **不失效**(除被修改节点) | 可能导致**全部重哈希**(桶扩容) | | 删除元素 | 仅使被删元素迭代器失效 | 仅使被删元素迭代器失效 | --- ### **关键选择指南** 1. 使用 `std::map` 当: - 需要**有序遍历**或**范围查询**(如 `lower_bound`) - 键类型无高效哈希函数 - 避免哈希冲突导致的性能波动 - 内存限制较小(哈希表桶数组有最小开销) 2. 使用 `std::unordered_map` 当: - 需要**O(1)平均访问性能** - 不关心元素顺序 - 有高质量哈希函数(或能自定义) - 内存充足(哈希表需要额外 20-50% 空间) --- ### **自定义键示例** ```cpp // 自定义键类型(需支持map比较或unordered_map哈希) struct Point { int x, y; bool operator==(const Point& p) const { return x == p.x && y == p.y; } }; // 为map定义比较器 struct PointCompare { bool operator()(const Point& a, const Point& b) const { return std::tie(a.x, a.y) < std::tie(b.x, b.y); } }; std::map<Point, std::string, PointCompare> pointMap; // 为unordered_map定义哈希器 struct PointHash { 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> pointUMap; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值