C/C++ 常用容器功能汇总

1. 说明

C 语言本身不提供标准通用容器模板,常见数据结构通常需要:

  • 手动实现;
  • 使用第三方库;
  • 使用平台提供的链表、哈希表等接口。

C++ 标准模板库 STL 提供了丰富的容器、迭代器和算法。现代 C++ 开发应优先使用标准容器。


2. 容器分类

2.1 序列容器

按元素排列顺序组织数据:

  • std::array
  • std::vector
  • std::deque
  • std::list
  • std::forward_list

2.2 有序关联容器

通常基于平衡搜索树,元素保持有序:

  • std::set
  • std::multiset
  • std::map
  • std::multimap

2.3 无序关联容器

通常基于哈希表,元素不保证顺序:

  • std::unordered_set
  • std::unordered_multiset
  • std::unordered_map
  • std::unordered_multimap

2.4 容器适配器

基于其他容器封装特定访问方式:

  • std::stack
  • std::queue
  • std::priority_queue

3. 容器总览

容器 主要特点 随机访问 头部插入 尾部插入 中间插入 查找
array 固定长度连续数组 O(1) 不支持 不支持 不支持 O(n)
vector 动态连续数组 O(1) O(n) 均摊 O(1) O(n) O(n)
deque 双端动态序列 O(1) O(1) O(1) O(n) O(n)
list 双向链表 不支持 O(1) O(1) O(1) O(n)
forward_list 单向链表 不支持 O(1) 不直接支持 O(1) O(n)
set 有序唯一键 不支持 --- --- O(log n) O(log n)
map 有序键值对 不支持 --- --- O(log n) O(log n)
unordered_set 哈希唯一键 不支持 --- --- 平均 O(1) 平均 O(1)
unordered_map 哈希键值对 不支持 --- --- 平均 O(1) 平均 O(1)

复杂度为典型情况。无序容器在严重哈希冲突时,最坏可能退化为 O(n)。


4. 通用成员函数

大多数 STL 容器支持以下接口。

cpp 复制代码
container.empty();       // 是否为空
container.size();        // 元素个数
container.clear();       // 清空
container.begin();       // 首元素迭代器
container.end();         // 尾后迭代器
container.cbegin();      // const 迭代器
container.cend();

遍历:

cpp 复制代码
for (const auto& item : container) {
    // 只读访问
}

修改元素:

cpp 复制代码
for (auto& item : container) {
    // 可修改访问
}

5. std::array

固定长度、连续存储、长度在编译期确定。

cpp 复制代码
#include <array>

std::array<int, 4> values{1, 2, 3, 4};

values[0] = 10;
int x = values.at(1);

特点:

  • 无动态内存分配;
  • 可使用 STL 算法;
  • 支持 size()begin()end()
  • at() 越界时抛出异常;
  • 适合固定维度数据、查找表、小型缓冲区。

6. std::vector

最常用的动态数组。

cpp 复制代码
#include <vector>

std::vector<int> values;
values.reserve(100);

values.push_back(10);
values.emplace_back(20);
values.pop_back();

int first = values.front();
int last = values.back();

常用接口:

cpp 复制代码
values.size();
values.capacity();
values.reserve(1000);
values.resize(100);
values.shrink_to_fit();
values.insert(values.begin(), 5);
values.erase(values.begin());

特点:

  • 内存连续,缓存友好;
  • 支持随机访问;
  • 尾部插入高效;
  • 扩容可能导致指针、引用和迭代器失效;
  • 中间插入删除需要移动后续元素。

通常没有特殊理由时,序列数据优先考虑 vector


7. std::deque

双端队列,支持头尾高效插入删除。

cpp 复制代码
#include <deque>

std::deque<int> data;

data.push_back(10);
data.push_front(5);
data.pop_back();
data.pop_front();

特点:

  • 头尾操作通常为 O(1);
  • 支持随机访问;
  • 元素一般不是一整块连续内存;
  • 不适合把 data() 当作连续缓冲区;
  • 常作为 queuestack 的底层容器。

8. std::list

双向链表。

cpp 复制代码
#include <list>

std::list<int> values{1, 2, 3};

auto it = values.begin();
++it;
values.insert(it, 10);
values.erase(it);

特点:

  • 已知位置时插入、删除为 O(1);
  • 不支持随机访问;
  • 每个节点有额外指针开销;
  • 缓存局部性通常较差;
  • 迭代器通常较稳定;
  • 支持 splice() 在链表间移动节点。

不要因为"中间插入快"就默认选择 list。实际工程中,vector 常因缓存优势表现更好。


9. std::forward_list

单向链表,比 list 更节省节点开销。

cpp 复制代码
#include <forward_list>

std::forward_list<int> values{1, 2, 3};

values.push_front(0);
values.insert_after(values.before_begin(), -1);
values.erase_after(values.before_begin());

特点:

  • 仅支持单向遍历;
  • 不提供常数时间 size()
  • 使用 insert_aftererase_after
  • 适合极低开销单向链式结构。

10. std::setstd::multiset

cpp 复制代码
#include <set>

std::set<int> unique_values;
unique_values.insert(3);
unique_values.insert(1);
unique_values.insert(3);  // 不会重复插入

std::multiset<int> repeated_values;
repeated_values.insert(3);
repeated_values.insert(3);

特点:

  • 自动排序;
  • set 键唯一;
  • multiset 允许重复;
  • 查找、插入、删除通常为 O(log n);
  • 适合范围查询和有序遍历。

11. std::mapstd::multimap

cpp 复制代码
#include <map>
#include <string>

std::map<std::string, int> scores;

scores["Alice"] = 90;
scores.insert({"Bob", 85});
scores.emplace("Carol", 95);

安全查找:

cpp 复制代码
auto it = scores.find("Alice");

if (it != scores.end()) {
    int score = it->second;
}

注意:

cpp 复制代码
int value = scores["Unknown"];

operator[] 在键不存在时会创建一个默认值元素。

只查询不插入时,可使用:

cpp 复制代码
int value = scores.at("Alice");

12. std::unordered_setstd::unordered_map

cpp 复制代码
#include <unordered_map>
#include <unordered_set>

std::unordered_set<int> ids{1, 2, 3};

std::unordered_map<int, std::string> names;
names.emplace(1, "camera");
names.emplace(2, "lidar");

特点:

  • 平均 O(1) 查找;
  • 不保证元素顺序;
  • 需要哈希函数和相等比较;
  • 扩容重新哈希会使迭代器失效;
  • 可通过 reserve() 降低重复扩容成本。
cpp 复制代码
names.reserve(1000);
names.max_load_factor(0.75f);

13. std::stack

后进先出 LIFO。

cpp 复制代码
#include <stack>

std::stack<int> st;

st.push(1);
st.push(2);

int top = st.top();
st.pop();

常用接口:

  • push
  • emplace
  • top
  • pop
  • empty
  • size

注意:pop() 不返回被删除的元素。


14. std::queue

先进先出 FIFO。

cpp 复制代码
#include <queue>

std::queue<int> q;

q.push(1);
q.push(2);

int first = q.front();
int last = q.back();

q.pop();

常用接口:

  • push
  • emplace
  • front
  • back
  • pop
  • empty
  • size

15. std::priority_queue

优先级队列,默认最大元素优先。

cpp 复制代码
#include <queue>
#include <vector>
#include <functional>

std::priority_queue<int> max_heap;
max_heap.push(3);
max_heap.push(10);
max_heap.push(5);

int max_value = max_heap.top();

最小堆:

cpp 复制代码
std::priority_queue<
    int,
    std::vector<int>,
    std::greater<int>
> min_heap;

典型用途:

  • Top-K;
  • Dijkstra;
  • 任务调度;
  • 实时选择最高优先级元素。

16. 常用 STL 算法

cpp 复制代码
#include <algorithm>
#include <numeric>
#include <vector>

std::vector<int> values{4, 1, 3, 2};

std::sort(values.begin(), values.end());

auto it = std::find(values.begin(), values.end(), 3);

int count = std::count(values.begin(), values.end(), 2);

int sum = std::accumulate(values.begin(), values.end(), 0);

常见算法:

  • sort
  • stable_sort
  • find
  • find_if
  • count
  • count_if
  • lower_bound
  • upper_bound
  • binary_search
  • remove_if
  • transform
  • accumulate

删除满足条件的元素:

cpp 复制代码
values.erase(
    std::remove_if(
        values.begin(),
        values.end(),
        [](int x) { return x < 0; }
    ),
    values.end()
);

C++20 可使用:

cpp 复制代码
std::erase_if(values, [](int x) { return x < 0; });

17. 容器选择建议

需求 推荐容器
默认动态序列 vector
固定长度数组 array
频繁头尾插入删除 deque
已知节点位置频繁链式操作 list
需要自动排序且键唯一 set
需要有序键值映射 map
需要快速查找,不关心顺序 unordered_map / unordered_set
后进先出 stack
先进先出 queue
始终获取最高或最低优先级 priority_queue

18. 工程注意事项

  1. 优先使用值语义和 RAII。
  2. 避免在容器中存储具有裸指针所有权的对象。
  3. 提前知道数据规模时,给 vectorunordered_map 调用 reserve()
  4. 只读遍历使用 const auto&,避免不必要拷贝。
  5. 插入复杂对象时优先考虑 emplace,但不要机械替换所有 push
  6. 修改容器结构后,注意迭代器、指针、引用是否失效。
  7. 对外暴露容器时,避免让调用方依赖具体底层类型。
  8. 根据访问模式、内存局部性和数据规模选择容器,而不只看理论复杂度。
相关推荐
峥嵘life2 小时前
Android16 311Y3 EAP-TLS 网络连接失败分析与修复总结
android·开发语言·人工智能·php
ShineWinsu2 小时前
对于C++:IO流状态的详细解析
c++·面试·io·cin·io流状态·goodbit·failbit
Lzg_na4 小时前
constexpr的优势
c++
mqiqe5 小时前
AgentScope Java Harness:4. 双层记忆系统 让 Agent 拥有真正的“长期大脑“
java·开发语言
gugucoding5 小时前
46. 【Java】JUC并发工具:让并发更简单
java·开发语言
王维同学5 小时前
进程模块枚举、映像身份与线程启动地址关联
c++·windows·安全
hold?fish:palm5 小时前
24 回文链表
数据结构·c++·链表
举手5 小时前
Dispatcher模块剖析
linux·c++
Nebula_g6 小时前
JavaSE基础语法:面向对象高级(代码中的成分)
java·开发语言·编程·javase·技术栈·高级语法
for_ever_love__6 小时前
python基础语法学习: 异常的传递性
开发语言·python·学习