在 C++ 标准库中,std::set
、std::map
和 std::unordered_map
是常用的关联容器,但它们在实现方式、性能和应用场景上有显著差异。以下是它们的核心区别:
1. 数据结构与有序性
-
std::set
/std::map
基于 红黑树(Red-Black Tree) 实现,元素(或键值对)严格有序(按升序排列)。std::set
:存储唯一键值,仅键可被访问。std::map
:存储键值对,键唯一,通过键快速查找值。
-
std::unordered_map
基于 哈希表(Hash Table) 实现,元素无序。键唯一,通过哈希函数快速定位值。
2. 时间复杂度
操作 | std::set /std::map | std::unordered_map |
---|---|---|
插入 | O(log n) | 平均 O(1),最坏 O(n) |
删除 | O(log n) | 平均 O(1),最坏 O(n) |
查找 | O(log n) | 平均 O(1),最坏 O(n) |
范围查询(如 lower_bound ) | O(log n + k)(k为结果数量) | 不支持有序范围查询 |
3. 内存占用
-
红黑树(
set/map
)
内存占用相对较高,但结构紧凑,节点存储父节点、子节点指针和颜色标记。 -
哈希表(
unordered_map
)
需要额外空间处理哈希冲突(如负载因子控制),通常内存占用更高,但实际访问速度更快。
4. 迭代器稳定性
-
std::set
/std::map
插入或删除元素时,只有被操作的节点的迭代器失效,其他迭代器不受影响。 -
std::unordered_map
插入可能导致哈希表 重新哈希(如扩容),此时所有迭代器都会失效。
5. 允许重复键?
- 所有容器均不允许重复键。若插入重复键:
std::set/map
:插入失败,返回false
。std::unordered_map
:覆盖旧值(可通过检查返回的std::pair<bool, T&>
判断是否已存在)。
6. 应用场景
-
std::set
需要唯一元素的有序集合,支持快速插入、删除和范围查询(如日期排序、唯一用户ID管理)。 -
std::map
需要键值对的有序映射,如配置项存储(键为字符串,值为整数)。 -
std::unordered_map
需要高速哈希查找的场景,如缓存系统、字典(键值对频繁访问且无需顺序)。
示例代码对比
#include <iostream>
#include <set>
#include <map>
#include <unordered_map>
int main() {
std::set<int> s = {3, 1, 4};
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
std::unordered_map<std::string, int> um = {{"x", 10}, {"y", 20}};
// set 和 map 是有序的
for (auto num : s) std::cout << num << " "; // 输出 1 3 4
for (auto& kv : m) std::cout << kv.first << ": " << kv.second << " "; // a:1 b:2
// unordered_map 无序
for (auto& kv : um) std::cout << kv.first << ": " << kv.second << " ";
// 可能输出 x:y 或 y:x 等不确定顺序
return 0;
}
总结
- 有序、稳定、范围查询 → 选
set
或map
。 - 快速哈希查找 → 选
unordered_map
。 - 内存敏感且数据量小 → 优先
set/map
;大数据量且追求速度 → 选unordered_map
。