std::map 使用注意事项

本文探讨了在C++中使用映射(maps)进行高效查找的两种方法,并对比了它们的性能。一种方法是在使用之前检查键是否存在于映射中,另一种方法是直接尝试获取键对应的值并检查是否成功。文章强调了正确使用这些技巧的重要性,以避免不必要的性能损失。

对于下面的场景,存在key就使用,否则返回null,有下面两种写法:

 if(m.count(key)>0)
 {
     return m[key];
 }
 return null;
 iter = m.find(key);
 if(iter!=m.end())
 {
     return iter->second;
 }
 return null;

1.这里需要注意:前一种方法很直观,但是效率差很多。因为前面的方法,需要执行两次查找。因此,推荐使用后一种方法。

2.不管用哪种都需要判断,是否存在,否则会默认增加一个指定的key值元素,对应的Second项则不可预知。

<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; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值