STL中的向量与列表

时间:2010-02-05 17:54:54

标签: c++ list vector stl

我在有效STL中注意到

  

vector是序列的类型   应该默认使用。

这是什么意思?似乎忽略效率vector可以做任何事情。

有人可以向我提供vector不是可行选项但必须使用list的情况吗?

17 个答案:

答案 0 :(得分:371)

<强>矢量:

  • 连续记忆。
  • 为未来的元素预先分配空间,因此需要额外的空间超出元素本身所需的空间。
  • 每个元素只需要元素类型本身的空间(没有额外的指针)。
  • 每次添加元素时都可以为整个矢量重新分配内存。
  • 最后的插入是不变的,摊销时间,但其他地方的插入是昂贵的O(n)。
  • 向量末尾的删除是恒定时间,但其余为O(n)。
  • 您可以随机访问其元素。
  • 如果向向量添加元素或从向量中删除元素,则迭代器无效。
  • 如果需要元素数组,可以轻松获取基础数组。

<强>列表:

  • 非连续记忆。
  • 没有预先分配的内存。列表本身的内存开销是不变的。
  • 每个元素都需要额外的空间用于保存元素的节点,包括指向列表中下一个元素和前一个元素的指针。
  • 永远不必因为添加元素而为整个列表重新分配内存。
  • 插入和删除都很便宜,无论它们出现在列表的哪个位置。
  • 将列表与拼接结合起来很便宜。
  • 您无法随机访问元素,因此获取列表中的特定元素可能会非常昂贵。
  • 即使您在列表中添加或删除元素,迭代器仍然有效。
  • 如果你需要一个元素数组,你必须创建一个新元素并将它们全部添加到它,因为没有底层数组。

一般情况下,当你不关心你正在使用什么类型的顺序容器时使用vector,但如果你在容器中的任何地方进行多次插入或擦除,那么你就是想要使用列表。或者,如果您需要随机访问,那么您将需要向量,而不是列表。除此之外,根据您的应用程序,您自然会需要其中一个,但总的来说,这些都是很好的指导。

答案 1 :(得分:84)

您希望将大量项目重复插入到序列末尾的任何位置的情况。

查看每种不同类型容器的复杂性保证:

What are the complexity guarantees of the standard containers?

答案 2 :(得分:29)

如果您不需要经常插入元素,那么矢量将更有效。它具有比列表更好的CPU缓存局部性。换句话说,访问一个元素会使非常可能导致下一个元素出现在缓存中,并且无需读取缓慢的RAM即可检索。

答案 3 :(得分:26)

这里的大多数答案都错过了一个重要的细节:为什么?

你想在收件人中保留什么?

如果它是int的集合,那么std::list将在每个场景中丢失,无论您是否可以重新分配,您只能从前面删除,等等。列表遍历较慢,每个插入会花费您与分配器的交互。准备一个例子是非常困难的,list<int>击败vector<int>。即便如此,deque<int>可能更好或更接近,而不仅仅是使用列表,这会产生更大的内存开销。

然而,如果你正在处理大而丑陋的数据 - 而且很少 - 你不想在插入时进行全面分配,并且由于重新分配造成的复制将是一场灾难 - 那么你可能会更好关闭list<UglyBlob>而不是vector<UglyBlob>

但是,如果你再次切换到vector<UglyBlob*>甚至vector<shared_ptr<UglyBlob> >,那么列表就会落后。

因此,访问模式,目标元素数量等仍会影响比较,但在我看来 - 元素大小 - 复制成本等。

答案 4 :(得分:16)

std :: list的一个特殊功能是拼接(将一部分或整个列表链接或移动到不同的列表中)。

或许如果您的内容复制起来非常昂贵。在这种情况下,例如,使用列表对集合进行排序可能会更便宜。

另请注意,如果集合很小(内容复制起来不是特别昂贵),即使您在任何地方插入和删除,矢量仍可能优于列表。列表单独分配每个节点,这可能比移动一些简单的对象要昂贵得多。

我认为没有非常严格的规则。这取决于你最想要对容器做什么,以及你期望容器的大小和包含的类型。向量通常胜过列表,因为它将其内容分配为单个连续块(它基本上是一个动态分配的数组,在大多数情况下,数组是保存一堆东西的最有效方法)。

答案 5 :(得分:13)

我班上的学生似乎无法向我解释何时使用向量更有效,但在建议我使用列表时他们看起来很开心。

这就是我理解的方式

<强>解释: 每个项目都包含下一个或上一个元素的地址,因此使用此功能,您可以随机化项目,即使它们未排序,顺序也不会更改:如果内存碎片化,则效率很高。 但它还有另一个非常大的优势:您可以轻松插入/删除项目,因为您唯一需要做的就是更改一些指针。 退税: 要读取随机单个项目,您必须从一个项目跳到另一个项目,直到找到正确的地址。

<强>载体: 当使用向量时,内存比常规数组更有条理:每个第n项存储在第(n-1)项之后和第(n + 1)项之前。 为什么它比列表更好? 因为它允许快速随机访问。 方法如下:如果您知道向量中项目的大小,并且它们在内存中是连续的,则可以轻松预测第n个项目的位置;你不必浏览列表中的所有项目来阅读你想要的那个项目,使用矢量,你可以直接阅读它,但你不能使用列表。 另一方面,修改矢量数组或更改值要慢得多。

列表更适合跟踪可在内存中添加/删除的对象。 当您想要从大量单个项目中访问元素时,向量更合适。

我不知道列表是如何优化的,但你必须知道如果你想要快速读取访问,你应该使用向量,因为STL紧固列表有多好,它在读取访问中不会那么快比矢量。

答案 6 :(得分:10)

基本上,vector是一个具有自动内存管理的数组。数据在内存中是连续的。试图在中间插入数据是一项代价高昂的操作。

在列表中,数据存储在不相关的内存位置。在中间插入不涉及复制一些数据以为新的数据腾出空间。

为了更具体地回答你的问题,我引用this page

  

向量通常是访问元素以及从序列末尾添加或删除元素的最有效时间。对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比deques和list更差,并且与列表相比具有更少的一致迭代器和引用。

答案 7 :(得分:9)

任何时候你都无法使迭代器失效。

答案 8 :(得分:8)

当序列中间有大量插入或删除时。例如记忆管理员。

答案 9 :(得分:4)

保留迭代器的有效性是使用列表的一个原因。另一种是当你不想在推送物品时重新分配矢量时。这可以通过智能使用reserve()来管理,但在某些情况下,使用列表可能更容易或更可行。

答案 10 :(得分:4)

如果要在容器之间移动对象,可以使用list::splice

例如,图形分区算法可以具有在递增数量的容器之间递归划分的恒定数量的对象。对象应初始化一次,并始终保留在内存中的相同位置。通过重新分配重新排列它们比重新分配要快得多。

编辑:当库准备实现C ++ 0x时,将子序列拼接到列表中的一般情况随着序列的长度变得线性复杂。这是因为splice(现在)需要迭代序列来计算其中元素的数量。 (因为列表需要记录其大小。)简单地计算和重新链接列表仍然比任何替代方案更快,并且拼接整个列表或单个元素是具有恒定复杂性的特殊情况。但是,如果你有很长的连接序列,你可能不得不四处寻找更好,老式,不合规的容器。

答案 11 :(得分:1)

必须使用list的唯一硬规则是您需要将指针分发到容器的元素。

vector不同,您知道元素的内存不会被重新分配。如果它可能那么你可能有指向未使用内存的指针,这最多是一个很大的禁忌,最坏的是SEGFAULT

(从技术角度来说,vector的{​​{1}}也可以正常运行,但在这种情况下,您正在仿效*_ptr,这只是语义。)

其他软规则与将元素插入容器中间的可能性能问题有关,因此list更可取。

答案 12 :(得分:1)

列表只是stl中的双向链接列表的包装,因此提供了d链接列表可能期望的功能,即O(1)插入和删除。 向量是具有传染性的数据序列,其作用类似于动态数组。PS-易于遍历。

答案 13 :(得分:1)

简单点-
最终,当您感到困惑时,请使用此流程图图像选择C ++中的容器(感谢我):- enter image description here

矢量-
 1.向量基于传染性记忆
 2.向量是处理小数据集的方法
 3.向量在数据集上遍历时执行速度最快
 4.向量插入删除在庞大的数据集上速度较慢,但​​对于非常快     小

列表-
 1.列表基于堆内存
 2.列表是处理非常庞大的数据集的方法
 3.列表在遍历较小数据集时相对较慢,但在     庞大的数据集
 4.列表插入删除在巨大的数据集上是快速的,而在较小的数据集上是缓慢的     

答案 14 :(得分:1)

在表格中总结答案以供快速参考:

<头>
矢量 列表
访问 更快 较慢
插入/删除操作 较慢 更快
内存分配 连续 不连续
尺寸预分配 需要保留 不需要预约
每个元素所需的空间 仅针对元素本身 用于元素和指向下一个
(以及可选的前一个元素)的指针

答案 15 :(得分:0)

vector list 的情况下,突出的主要区别如下:

向量

  • 向量在连续内存中存储其元素。因此,随机 向量内部可以进行访问,这意味着访问 向量的元素非常快,因为我们可以简单地将 具有用于访问该元素的项目索引的基址。实际上, 为此仅需要O(1)或恒定时间。

  • 由于向量基本上是包装一个数组,因此每次插入 元素放入向量(动态数组)中,必须通过 查找新的连续内存块以容纳新的 元素是费时的。

  • 它不会消耗额外的内存来存储指向其他对象的任何指针 其中的元素。

列表

  • 一个列表将其元素存储在不连续的内存中。因此, 列表内无法进行随机访问,这意味着 访问其元素,我们必须使用指针并遍历 相对于向量而言较慢的列表。这需要O(n)或线性时间,即 比O(1)慢。

  • 由于列表使用不连续的内存,因此插入列表所用的时间 列表中的元素比其元素更有效 向量对应项,因为避免了内存的重新分配。

  • 它消耗额外的内存来存储指向和之前的元素的指针 在特定元素之后。

因此,请牢记这些差异,我们通常会考虑内存,频繁的随机访问插入来确定的获胜者向量与列表”。

答案 16 :(得分:0)

列表是双重链接列表,因此很容易插入和删除元素。我们只需要更改几个指针,而在向量中,如果我们想在中间插入一个元素,则之后的每个元素都必须移动一个索引。同样,如果向量的大小已满,则必须首先增加其大小。因此,这是一项昂贵的操作。 因此,在这种情况下,凡需要频繁执行插入和删除操作的地方,都应使用列表。