本文系统总结 C++ STL 中最常用的五种容器------vector、map、set、unordered_map、unordered_set 的常用方法,涵盖构造、迭代、容量、元素访问、查找、修改等操作,并附带代码示例与时间复杂度说明,最后给出一张横向对比表,帮助你在实际开发中快速查阅与选型。
一、vector 动态数组
1.1 简介
vector 是一段连续内存的动态数组,支持随机访问,尾部增删效率高(均摊 O(1)),中间/头部增删为 O(n)。它是日常开发中使用频率最高的容器。
头文件: <vector>
1.2 构造
vector<int> v1; // 空容器
vector<int> v2(n); // n 个默认值(0)
vector<int> v3(n, 10); // n 个 10
vector<int> v4 = {1, 2, 3, 4, 5}; // 初始化列表
vector<int> v5(v4); // 拷贝构造
vector<int> v6(v4.begin(), v4.begin() + 3); // 区间构造
1.3 迭代器
| 方法 |
说明 |
begin() / end() |
首元素 / 尾后迭代器 |
rbegin() / rend() |
反向首 / 反向尾后 |
cbegin() / cend() |
只读版本(C++11) |
for (auto it = v.begin(); it != v.end(); ++it)
cout << *it << " ";
// 范围 for 循环
for (const auto& x : v)
cout << x << " ";
1.4 容量与大小
| 方法 |
说明 |
size() |
当前元素个数 |
capacity() |
已分配的存储容量 |
empty() |
是否为空 |
resize(n) / resize(n, val) |
改变大小,多则填默认值/val |
reserve(n) |
预分配容量(不改变 size) |
shrink_to_fit() |
请求释放未使用内存 |
技巧: 已知最终元素数量时,提前 reserve() 可避免多次重新分配,显著提升性能。
1.5 元素访问
| 方法 |
说明 |
operator[i] |
下标访问(不越界检查) |
at(i) |
下标访问(越界抛异常) |
front() |
首元素 |
back() |
尾元素 |
data() |
底层数组指针 |
v[0] // 快速访问
v.at(0) // 安全访问,越界抛 std::out_of_range
1.6 修改操作
| 方法 |
说明 |
push_back(val) |
尾部添加元素 |
pop_back() |
尾部删除元素 |
insert(pos, val) |
在 pos 处插入 |
insert(pos, n, val) |
插入 n 个 val |
insert(pos, first, last) |
插入区间 |
emplace(pos, args...) |
原地构造插入(C++11) |
emplace_back(args...) |
原地构造尾插(C++11) |
erase(pos) / erase(first, last) |
删除元素 |
clear() |
清空 |
swap(other) |
交换两个容器 |
assign(n, val) / assign(first, last) |
赋值替换 |
emplace vs push_back: emplace_back 直接在内存中原地构造对象,避免了临时对象的拷贝/移动,适合复杂对象。
1.7 完整示例
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1, 2, 3};
v.push_back(4); // {1,2,3,4}
v.emplace_back(5); // {1,2,3,4,5}
v.insert(v.begin(), 0); // {0,1,2,3,4,5}
v.pop_back(); // {0,1,2,3,4}
v.erase(v.begin()); // {1,2,3,4}
for (int i = 0; i < v.size(); i++)
cout << v[i] << " "; // 1 2 3 4
return 0;
}
1.8 时间复杂度
| 操作 |
复杂度 |
随机访问 operator[] / at |
O(1) |
push_back / pop_back |
均摊 O(1) |
insert / erase(中间) |
O(n) |
| 查找(无序) |
O(n) |
二、map 有序映射
2.1 简介
map 是基于红黑树 的有序键值对容器,键唯一且自动排序。查找、插入、删除均为 O(log n)。键需支持 operator<。
头文件: <map>
2.2 构造
map<int, string> m1; // 空
map<int, string> m2 = {{1, "apple"}, {2, "banana"}}; // 初始化列表
map<int, string> m3(m2); // 拷贝
map<int, string> m4(m2.begin(), m2.end()); // 区间构造
2.3 迭代器
map 的迭代器是双向迭代器,按 key 升序遍历。
for (auto& [key, val] : m) // C++17 结构化绑定
cout << key << " -> " << val << endl;
2.4 容量
| 方法 |
说明 |
size() |
元素个数 |
empty() |
是否为空 |
max_size() |
最大容量 |
2.5 元素访问
| 方法 |
说明 |
operator[key] |
访问/插入(key 不存在则插入默认值) |
at(key) |
访问(key 不存在则抛异常) |
注意: operator[] 在 key 不存在时会自动插入默认构造的值,这在只想查找时会意外修改容器。若仅查找,应使用 at() 或 find()。
2.6 查找
| 方法 |
说明 |
find(key) |
返回指向 key 的迭代器,未找到返回 end() |
count(key) |
key 的个数(map 中为 0 或 1) |
contains(key) |
是否存在(C++20) |
lower_bound(key) |
第一个 >= key 的迭代器 |
upper_bound(key) |
第一个 > key 的迭代器 |
equal_range(key) |
返回 [lower, upper) 区间 |
auto it = m.find(2);
if (it != m.end())
cout << it->second << endl; // banana
if (m.count(1)) // 存在返回 1
cout << "found" << endl;
2.7 修改操作
| 方法 |
说明 |
insert({key, val}) |
插入键值对(key 存在则不覆盖) |
insert_or_assign(key, val) |
插入或赋值(C++17) |
emplace(key, val) |
原地构造 |
erase(key) / erase(pos) |
删除 |
clear() |
清空 |
swap(other) |
交换 |
map<int, string> m;
m[1] = "apple"; // operator[] 插入+赋值
m.insert({2, "banana"}); // insert 插入
m.emplace(3, "cherry"); // 原地构造
m.erase(1); // 删除 key=1
2.8 时间复杂度
| 操作 |
复杂度 |
| 查找 / 插入 / 删除 |
O(log n) |
| 遍历 |
O(n) |
三、set 有序集合
3.1 简介
set 是基于红黑树 的有序集合,元素唯一且自动排序。与 map 类似,但只存 key 不存 value。底层实现与 map 相同,操作复杂度一致。
头文件: <set>
3.2 构造
set<int> s1; // 空
set<int> s2 = {3, 1, 4, 1, 5}; // {1,3,4,5}(自动去重排序)
set<int> s3(s2); // 拷贝
set<int> s4(s2.begin(), s2.end()); // 区间构造
set<int, greater<int>> s5 = {3, 1, 2}; // 降序排列 {3,2,1}
3.3 容量
| 方法 |
说明 |
size() |
元素个数 |
empty() |
是否为空 |
3.4 迭代器
按升序遍历(或自定义排序),双向迭代器。
for (const auto& x : s)
cout << x << " "; // 1 3 4 5
3.5 查找
| 方法 |
说明 |
find(val) |
返回指向 val 的迭代器 |
count(val) |
个数(0 或 1) |
contains(val) |
是否存在(C++20) |
lower_bound(val) |
第一个 >= val |
upper_bound(val) |
第一个 > val |
equal_range(val) |
[lower, upper) 区间 |
set<int> s = {1, 3, 5, 7, 9};
auto it = s.lower_bound(5); // 指向 5
auto it2 = s.upper_bound(5); // 指向 7
3.6 修改操作
| 方法 |
说明 |
insert(val) |
插入元素 |
emplace(args...) |
原地构造插入 |
erase(val) / erase(pos) |
删除 |
clear() |
清空 |
swap(other) |
交换 |
set<int> s;
s.insert(3);
s.insert(1);
s.insert(3); // 重复,不会插入
s.erase(1);
3.7 时间复杂度
| 操作 |
复杂度 |
| 查找 / 插入 / 删除 |
O(log n) |
| 遍历 |
O(n) |
四、unordered_map 哈希映射
4.1 简介
unordered_map 是基于哈希表 的键值对容器,键唯一但无序 。平均查找、插入、删除为 O(1),最坏情况为 O(n)(哈希冲突)。当不需要有序性时,性能优于 map。
头文件: <unordered_map>
4.2 构造
unordered_map<int, string> m1;
unordered_map<int, string> m2 = {{1, "apple"}, {2, "banana"}};
unordered_map<int, string> m3(m2);
4.3 容量
| 方法 |
说明 |
size() |
元素个数 |
empty() |
是否为空 |
max_size() |
最大容量 |
4.4 元素访问
| 方法 |
说明 |
operator[key] |
访问/插入(key 不存在则插入默认值) |
at(key) |
访问(不存在抛异常) |
4.5 查找
| 方法 |
说明 |
find(key) |
返回迭代器,未找到返回 end() |
count(key) |
0 或 1 |
contains(key) |
是否存在(C++20) |
equal_range(key) |
返回匹配范围 |
注意: unordered_map 没有 lower_bound / upper_bound,因为元素无序。
4.6 修改操作
| 方法 |
说明 |
insert({key, val}) |
插入 |
emplace(key, val) |
原地构造 |
erase(key) / erase(pos) |
删除 |
clear() |
清空 |
swap(other) |
交换 |
4.7 桶与哈希策略
| 方法 |
说明 |
bucket_count() |
桶数量 |
bucket(key) |
key 所在桶编号 |
bucket_size(n) |
第 n 个桶的元素数 |
load_factor() |
装载因子 = size / bucket_count |
max_load_factor() |
最大装载因子阈值 |
rehash(n) |
设置桶数(rehash) |
reserve(n) |
预留空间(自动 rehash) |
unordered_map<int, string> m;
m.reserve(1000); // 预留空间,减少 rehash 次数
4.8 完整示例
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
int main() {
unordered_map<string, int> wordCount;
wordCount["hello"] = 1;
wordCount["world"] = 2;
wordCount.emplace("cpp", 3);
// 遍历(无序)
for (const auto& [word, count] : wordCount)
cout << word << ": " << count << endl;
// 查找
if (wordCount.find("hello") != wordCount.end())
cout << "found hello" << endl;
return 0;
}
4.9 时间复杂度
| 操作 |
平均 |
最坏 |
| 查找 / 插入 / 删除 |
O(1) |
O(n) |
| 遍历 |
O(n) |
O(n) |
五、unordered_set 哈希集合
5.1 简介
unordered_set 是基于哈希表 的无序集合,元素唯一,不排序。与 unordered_map 底层实现一致,但仅存 key。平均 O(1) 的增删查。
头文件: <unordered_set>
5.2 构造
unordered_set<int> s1;
unordered_set<int> s2 = {1, 3, 5, 3, 7}; // {1,3,5,7}(去重,无序)
unordered_set<int> s3(s2);
5.3 容量
| 方法 |
说明 |
size() |
元素个数 |
empty() |
是否为空 |
5.4 迭代器
遍历顺序不确定(由哈希函数决定),前向迭代器。
for (const auto& x : s)
cout << x << " "; // 顺序不确定,如 5 1 7 3
5.5 查找
| 方法 |
说明 |
find(val) |
返回迭代器 |
count(val) |
0 或 1 |
contains(val) |
是否存在(C++20) |
equal_range(val) |
匹配范围 |
5.6 修改操作
| 方法 |
说明 |
insert(val) |
插入 |
emplace(args...) |
原地构造 |
erase(val) / erase(pos) |
删除 |
clear() |
清空 |
swap(other) |
交换 |
5.7 桶与哈希策略
| 方法 |
说明 |
bucket_count() |
桶数量 |
bucket(val) |
val 所在桶 |
load_factor() |
装载因子 |
rehash(n) |
重新哈希 |
reserve(n) |
预留空间 |
5.8 完整示例
#include <unordered_set>
#include <iostream>
using namespace std;
int main() {
unordered_set<int> s = {5, 3, 1, 7, 3}; // {1,3,5,7}
s.insert(9);
s.erase(3);
for (const auto& x : s)
cout << x << " "; // 无序输出
cout << (s.count(7) ? "yes" : "no") << endl;
return 0;
}
5.9 时间复杂度
| 操作 |
平均 |
最坏 |
| 查找 / 插入 / 删除 |
O(1) |
O(n) |
| 遍历 |
O(n) |
O(n) |
六、横向对比总表
| 特性 |
vector |
map |
set |
unordered_map |
unordered_set |
| 底层结构 |
动态数组 |
红黑树 |
红黑树 |
哈希表 |
哈希表 |
| 有序性 |
按插入序 |
按 key 排序 |
排序 |
无序 |
无序 |
| 元素唯一性 |
可重复 |
key 唯一 |
唯一 |
key 唯一 |
唯一 |
| 随机访问 |
O(1) |
仅 operator[]/at |
不支持 |
仅 operator[]/at |
不支持 |
| 查找 |
O(n) |
O(log n) |
O(log n) |
O(1) 平均 |
O(1) 平均 |
| 插入 |
尾部 O(1) |
O(log n) |
O(log n) |
O(1) 平均 |
O(1) 平均 |
| 删除 |
尾部 O(1) |
O(log n) |
O(log n) |
O(1) 平均 |
O(1) 平均 |
| 头文件 |
<vector> |
<map> |
<set> |
<unordered_map> |
<unordered_set> |
| 迭代器类型 |
随机访问 |
双向 |
双向 |
前向 |
前向 |
| lower/upper_bound |
支持 |
支持 |
支持 |
不支持 |
不支持 |
| 内存开销 |
小 |
较大 |
较大 |
中等 |
中等 |
七、选型建议
- 需要随机访问 + 尾部增删 →
vector(默认首选)
- 需要有序键值对 →
map
- 需要有序去重集合 →
set
- 需要高频查找键值对(不需排序) →
unordered_map
- 需要高频查找去重集合(不需排序) →
unordered_set
- 元素数量已知 → 提前
reserve() 减少 rehash/扩容开销
- 自定义类型作为 key →
map/set 需定义 operator<;unordered_map/unordered_set 需提供哈希函数和 operator==
自定义类型作为 unordered_set key 的示例
#include <unordered_set>
#include <string>
using namespace std;
struct Point {
int x, y;
bool operator==(const Point& o) const {
return x == o.x && y == o.y;
}
};
// 自定义哈希函数
struct PointHash {
size_t operator()(const Point& p) const {
return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1);
}
};
int main() {
unordered_set<Point, PointHash> points;
points.insert({1, 2});
points.insert({3, 4});
return 0;
}
八、通用方法速查
以下方法五个容器基本都支持:
| 方法 |
说明 |
size() |
元素个数 |
empty() |
是否为空 |
clear() |
清空 |
swap(other) |
交换两个容器 |
begin() / end() |
迭代器 |
insert(...) |
插入 |
emplace(...) |
原地构造插入 |
erase(...) |
删除 |
find(key) |
查找(vector 无此方法) |
count(key) |
计数(vector 无此方法) |
总结
| 场景 |
推荐容器 |
| 动态数组 / 栈 / 缓冲区 |
vector |
| 有序字典 / 范围查询 |
map |
| 有序去重 / 范围查询 |
set |
| 高频 KV 查找 / 计数 |
unordered_map |
| 高频去重查找 |
unordered_set |
核心原则: 需要有序 → 选红黑树系列(map/set);追求性能且无需排序 → 选哈希系列(unordered_map/unordered_set);需要随机访问 + 尾部增删 → 选 vector。
希望本文能帮助你在日常开发中快速查阅和选型。如有错误或遗漏,欢迎评论区交流指正!
本文档参考标准: cppreference.com