vector连续存储支持O(1)随机访问,list双向链表仅支持O(n)顺序访问;vector尾插均摊O(1)但中间操作O(n),list任意位置插入删除均为O(1)(需已有迭代器);vector迭代器易失效,list迭代器极稳定;vector缓存友好、内存紧凑,list内存开销大、易cache miss;真正需list的场景极少,如splice、LRU cache等。

vector 是连续内存,list 是节点分散存储
这是最根本的区别。vector 底层用一块连续的堆内存(类似动态数组),list 是双向链表,每个元素(节点)单独分配,通过 next 和 prev 指针链接。
这意味着:vector 支持 O(1) 随机访问(v[i]),list 只能顺序遍历(std::advance(it, n) 是 O(n));vector 尾插均摊 O(1),但中间插入/删除是 O(n)(要移动后续元素);list 在任意位置插入/删除都是 O(1),前提是已有迭代器指向该位置。
常见误判:以为 list::push_back() 比 vector::push_back() 快——其实两者尾插都很快,但 vector 的连续性带来更好的 CPU 缓存局部性,真实场景下往往更快。
迭代器失效规则完全不同
vector 迭代器在扩容、插入或删除时极易失效:
立即学习“C++免费学习笔记(深入)”;
-
push_back()可能触发 reallocation → 所有迭代器、引用、指针全部失效 -
erase(pos)仅使pos及之后的迭代器失效 -
insert()若不扩容,只使插入点及之后迭代器失效;若扩容,全部失效
list 迭代器非常稳定:
- 只有被
erase()的那个节点的迭代器失效 -
push_front()、push_back()、insert()不影响任何其他迭代器 - 甚至
splice()移动节点也不失效迭代器
面试常考陷阱:for (auto it = l.begin(); it != l.end(); ++it) { if (*it == x) l.erase(it); } —— 这段代码在 list 中会崩溃,因为 erase() 返回的是下一个有效迭代器,而 ++it 会对已失效的 it 操作。正确写法是 it = l.erase(it)。
内存开销与缓存友好性差异显著
list 每个元素额外携带两个指针(8 字节 × 2 = 16 字节,在 64 位系统),如果存的是 int(4 字节),内存浪费超 4 倍;vector 几乎无额外开销(仅 3 个指针:start、finish、end_of_storage)。
更重要的是缓存:连续内存让 vector 遍历时 CPU 能预取数据,list 节点随机分布,每次跳转都可能引发 cache miss。实测遍历百万 int,vector 通常比 list 快 5–10 倍。
所以除非你明确需要频繁在中间做插入/删除,且不关心遍历性能和内存占用,否则别默认选 list。C++ 标准库甚至不推荐用 list,std::deque 或 std::vector + erase-remove 更常用。
哪些操作 list 真的不可替代?
真正需要 list 的场景极少,但存在:
- 需要把一个节点从一个
list搬到另一个,且要求 O(1)、不拷贝(用splice()) - 多个线程各自持有不同节点的迭代器,需长期稳定(
list迭代器不因他人增删而失效) - 实现 LRU cache 时,用
list+unordered_map组合,可 O(1) 移动节点到头部::iterator>
注意:list 不支持 operator[],没有 capacity(),不能用 data() 获取原始指针——这些都不是缺陷,而是设计取舍。别试图把它当数组用。
实际项目里,95% 的“我以为需要 list”的情况,最后都用 vector 或 deque 更好。面试时说出这点,比背熟接口更有说服力。











