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. 根据访问模式、内存局部性和数据规模选择容器,而不只看理论复杂度。
相关推荐
猫头虎1 小时前
什么是ZCode for GLM-5.2?
开发语言·人工智能·python·科技·算法·ai编程·ai写作
choumin2 小时前
创建型模式——原型模式
c++·设计模式·原型模式·创建型模式
bksczm2 小时前
Linux之日志和线程池、内存池
java·开发语言
三十岁老牛再出发2 小时前
07.26每日总结
linux·c语言·mysql
笨蛋不要掉眼泪2 小时前
Java虚拟机:对象复活、引用强度与Stop-The-World
java·开发语言·jvm
布朗克1682 小时前
Go 入门到精通-33-unsafe 与 CGO
开发语言·后端·golang·unsafe·cgo
个 人 练 习 生2 小时前
strcmp与strstr函数的模拟实现
c语言·开发语言·经验分享·学习·程序人生
157092511343 小时前
【无标题】
开发语言·python·算法
xieliyu.3 小时前
MySQL 存储过程详解:概念、创建与删除全教程
开发语言·数据库·mysql