在C++标准模板库STL中,list和vector都是常用的序列式容器,两者的底层实现逻辑完全不同,因此性能表现和适用场景也存在显著差异,开发者需要根据实际需求合理选择。

底层数据结构差异
vector的底层是连续的内存空间,本质是一个动态数组,元素在内存中依次排列,支持随机访问。list的底层是双向链表,每个节点包含数据域、前驱指针和后继指针,节点在内存中离散分布,不支持随机访问。
常用操作性能对比
我们可以从增删改查几个核心操作维度,对比两者的性能表现:
| 操作类型 | vector | list |
|---|---|---|
| 随机访问 | 时间复杂度O(1),通过下标直接定位元素 | 时间复杂度O(n),需要遍历链表查找 |
| 尾部插入/删除 | 平均O(1),空间不足时扩容需要O(n)复制元素 | 时间复杂度O(1),只需要修改指针指向 |
| 中间插入/删除 | 时间复杂度O(n),需要移动后续所有元素 | 时间复杂度O(1),只需要修改相邻节点的指针 |
| 内存占用 | 额外开销小,只存储元素本身,可能有预留空间浪费 | 每个节点额外存储两个指针,内存开销更大 |
代码示例验证
下面通过一段测试代码,直观展示两者在随机访问和中间插入操作上的性能差异:
#include <iostream>
#include <vector>
#include <list>
#include <chrono>
int main() {
const int TEST_SIZE = 100000;
std::vector<int> test_vec;
std::list<int> test_list;
// 先填充100000个元素
for (int i = 0; i < TEST_SIZE; ++i) {
test_vec.push_back(i);
test_list.push_back(i);
}
// 测试随机访问性能
auto start = std::chrono::high_resolution_clock::now();
int sum = 0;
for (int i = 0; i < TEST_SIZE; ++i) {
sum += test_vec[i]; // vector随机访问
}
auto end = std::chrono::high_resolution_clock::now();
auto vec_access_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "vector随机访问耗时: " << vec_access_time.count() << "微秒" << std::endl;
start = std::chrono::high_resolution_clock::now();
sum = 0;
auto it = test_list.begin();
for (int i = 0; i < TEST_SIZE; ++i) {
sum += *it; // list需要遍历访问
++it;
}
end = std::chrono::high_resolution_clock::now();
auto list_access_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "list随机访问耗时: " << list_access_time.count() << "微秒" << std::endl;
// 测试中间插入性能
start = std::chrono::high_resolution_clock::now();
test_vec.insert(test_vec.begin() + TEST_SIZE/2, 999); // vector中间插入
end = std::chrono::high_resolution_clock::now();
auto vec_insert_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "vector中间插入耗时: " << vec_insert_time.count() << "微秒" << std::endl;
start = std::chrono::high_resolution_clock::now();
auto list_it = test_list.begin();
for (int i = 0; i < TEST_SIZE/2; ++i) ++list_it;
test_list.insert(list_it, 999); // list中间插入
end = std::chrono::high_resolution_clock::now();
auto list_insert_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "list中间插入耗时: " << list_insert_time.count() << "微秒" << std::endl;
return 0;
}
运行上述代码可以看到,vector的随机访问耗时远低于list,而list的中间插入耗时远低于vector,和理论分析的结论一致。
选型建议
结合两者的特性,我们可以按照以下场景选择容器:
- 如果需要频繁随机访问元素,或者主要在尾部进行增删操作,优先选择vector,比如存储数组数据、实现栈结构等场景。
- 如果需要频繁在中间位置插入或删除元素,且不需要随机访问,优先选择list,比如实现链表结构、频繁调整元素顺序的场景。
- 如果元素数量较少,且操作频率不高,两者的性能差异可以忽略,选择vector即可,因为vector的内存局部性更好,缓存命中率更高。
注意:vector扩容时会重新分配内存并复制所有元素,如果提前知道元素大致数量,可以通过reserve方法预留空间,减少扩容带来的性能损耗。