你何时喜欢使用std :: list <t>而不是std :: vector <t>?</t> </t>

时间:2011-02-20 12:18:20

标签: c++ stl containers

我自己从未使用std::list<T>。当我们已经拥有std::vector<T>时,我想知道人们何时使用它,就像具有连续内存的数组一样。当我们需要顺序容器时,std::vector似乎是一个完美的选择!

所以我的问题是

  • 首选{/ em> std::list超过std::vector的时间?为什么呢?
  • 您何时首选 std::vector而不是std::list?为什么?

如果有性能考虑,请列出详细说明/信息。

如果可能,请引用一些参考文献,以支持您的回答。

11 个答案:

答案 0 :(得分:29)

  

所以我的问题是:你更喜欢 std::list而不是std::vector

当我在性能敏感区域中需要顺序容器时, 分析 显示std::list更快。

到目前为止,这从未发生在我身上。

(当我不得不在中间存放大量插入/移除物体时,我可能会首先尝试std::list。但是,在实践中,我从未遇到过这样的用途 - 案例。)

答案 1 :(得分:15)

列表更适合插入或删除中间的任何位置,向量最好插入到最后。

矢量也更适合访问元素。

这是他们实施方式的人工制品。

因此,如果集合变化很小(与访问相比)或者更改集中在最后,我会使用向量。

如果变化的数量很大(与访问相比)并且它们不在最后,我会使用一个列表。

举例来说,在程序启动时读取集合并且几乎不会更改它(或者如果更改是添加到结尾),这将是向量的良好候选。

另一方面,对于一个特别受欢迎且善变的摇滚明星的电话簿应用程序,我正在寻找列表。实际上我会关注数据库连接,但这是我能在短时间内提出的最好的例子: - )

至于引用,最新的C ++ 0x草案在部分(23.3.4,列表)中说明:

  

列表是一个序列容器,它支持双向迭代器,并允许在序列中的任何位置进行常量时间插入和擦除操作,并自动处理存储管理。与矢量和deques不同,不支持对列表元素的快速随机访问。

第23.3.5节(关于载体):

  

vector是一个支持随机访问迭代器的序列容器。此外,它支持(摊销)最后的恒定时间插入和擦除操作;在中间插入和擦除需要线性时间。

答案 2 :(得分:11)

在std :: list和std :: vector之间进行选择时,需要考虑一些权衡因素。另外std :: list不是关于连续内存的,如果你无法承受迭代器失效或者你需要在begin / middle / end中进行分摊的常量时间插入,它会非常有用。

答案 3 :(得分:7)

我首选std::list的唯一(几次)是由于list::splice成员函数。如果您在列表中或列表之间的子范围内进行混洗,则此操作可能比使用std::vector快得多。

答案 4 :(得分:4)

我不需要重复基础知识,但我学到的很难的方法是,如果插入性能相关并且你有“大”对象,那么你应该真的考虑std: :list,甚至如果你只是在最后插入。 (好吧,列表或可能是智能指针/ ptr_vector的向量。)

我们有一些使用案例,我们必须建立一个先验未知大小的多个小std :: string结构的集合,并使用std :: vector完全杀死插入性能添加了一个不可忽视的内存开销。

未知计数插入场景中std :: vector的问题是:

  • 由于向量将始终过度分配,因此对于典型实现,您需要支付50%的空间开销(对于具有微小100,000个元素的对象具有的单个向量,例如24字节(MSVC),可以具有空间2MB的开销。对于具有更多字符串或其他更大的成员的更大对象,它将成倍增加。)
  • 每次向量必须重新分配时,它必须复制周围的所有对象,如果你有任何轻微的复杂,这并不便宜。显然,移动语义会有所帮助,但如果对象本身很大,重复的副本可能仍然是相关的。如果在构建向量期间复制所有对象多次,则平均摊销插入时间(在结尾处)理论上是不变的并不重要。你可能会注意到这种表现。
  • 对象变得非常快:在MSVC上只有两个空std :: string的结构已经不可移动 48个字节。

这不是为了bash std :: vector,我仍然默认使用它 - 但知道你的数据结构会有什么期待!

答案 5 :(得分:2)

除了其他答案, 基于节点的容器(list /关联容器)可以提供强大的功能 例外保证。
即使容器变异操作(例如插入)抛出一个 异常,保留元素的所有指针/引用/迭代器 有效的。
但是,线性存储器(分段连续存储器单元)容器可以 只提供基本保证。 当插入抛出时,即使插入没有实际执行, 指针/引用/迭代器可能无效 (虽然容器本身可以安全地被破坏)。

答案 6 :(得分:1)

当您需要经常修改除前面或后面以外的其他位置的序列时,可以使用std :: list。在std :: std :: list中的std :: vector中,此类操作的开销很大。

答案 7 :(得分:1)

当其无效语义和性能特征符合您的要求时使用列表。

List可以在O(1)中的任何地方插入/擦除/拼接,这不会使任何迭代器无效。除了最后,向量是用于插入/擦除的O(n),并且如果大小&lt;容量;矢量不能拼接。例如,在另一个答案中提到了缓存局部性,性能甚至比这更微妙。

答案 8 :(得分:1)

std::list几乎完全是我唯一的vector不共享的属性,而且知道我对list的指针始终有效。因此,我可以使用它来包含我在单个区域中需要的任何资产的所有实例,同时还借用对其他对象的引用。我能想到的最好的例子是图像加载器,它只在内存中保留一个像素副本,同时将指针指向多个实体,以便它们都可以从中抽取。

所有vector都是O(1)访问时间。虽然这听起来像是一笔巨大的资产,但实际上我很少需要在从“第一个”到“最后一个”的步骤之外访问数据结构中的项目

答案 9 :(得分:1)

有些人会说,你应该never ever ever use linked list in your code again。 ;)

一个问题是,向量具有更好的引用位置,并且由此产生的大的性能提升在许多情况下将超过更多(算法)高效操作的优势,例如删除并插入链表。

(请参阅上面的博客文章,了解有关此特定问题的更多讨论,以及基准测试等。)

因此,std :: vector通常会胜过std :: list,即使你正在进行一定数量的操作,列表会更自然(例如,任意元素删除,插入任意位置或拼接)

但请注意,即使你做了很多这类操作,你最好还是在std :: vector的连续缓冲区中“托管”你的列表。

我在本博文中更详细地讨论了这个问题,并附有一些图表和代码示例:http://upcoder.com/12/vector-hosted-lists/

在特定情况下,当您需要删除任意元素,但不需要插入任意位置或拼接时,完整链接列表的一个很好的替代方法可以是将条目标记为“死”并跳过迭代期间这些死的条目。

答案 10 :(得分:0)

当你进行大量删除/插入时,你应该使用一个列表。

如果元素的总大小不会发生很大变化,并且您进行了一些交换,则可以使用向量。