简体   繁体   English

STL 中的向量与列表

[英]vector vs. list in STL

I noticed in Effective STL that我在 Effective STL 中注意到

vector is the type of sequence that should be used by default. vector 是默认情况下应该使用的序列类型。

What's does it mean?这是什么意思? It seems that ignore the efficiency vector can do anything.似乎忽略效率vector可以做任何事情。

Could anybody offer me a scenario where vector is not a feasible option but list must be used?任何人都可以向我提供一个场景,其中vector不是一个可行的选择,但必须使用list吗?

vector:向量:

  • Contiguous memory.连续内存。
  • Pre-allocates space for future elements, so extra space required beyond what's necessary for the elements themselves.为未来的元素预先分配空间,因此需要超出元素本身所需的额外空间。
  • Each element only requires the space for the element type itself (no extra pointers).每个元素只需要元素类型本身的空间(没有额外的指针)。
  • Can re-allocate memory for the entire vector any time that you add an element.可以在添加元素时为整个向量重新分配内存。
  • Insertions at the end are constant, amortized time, but insertions elsewhere are a costly O(n).最后的插入是固定的,摊销时间,但其他地方的插入是一个代价高昂的 O(n)。
  • Erasures at the end of the vector are constant time, but for the rest it's O(n).向量末尾的擦除是常数时间,但其余时间为 O(n)。
  • You can randomly access its elements.您可以随机访问其元素。
  • Iterators are invalidated if you add or remove elements to or from the vector.如果向向量添加元素或从向量中删除元素,则迭代器将失效。
  • You can easily get at the underlying array if you need an array of the elements.如果需要元素数组,则可以轻松获取底层数组。

list:列表:

  • Non-contiguous memory.非连续内存。
  • No pre-allocated memory.没有预先分配的内存。 The memory overhead for the list itself is constant.列表本身的内存开销是恒定的。
  • Each element requires extra space for the node which holds the element, including pointers to the next and previous elements in the list.每个元素都需要为保存该元素的节点提供额外的空间,包括指向列表中下一个和上一个元素的指针。
  • Never has to re-allocate memory for the whole list just because you add an element.永远不必因为添加元素而为整个列表重新分配内存。
  • Insertions and erasures are cheap no matter where in the list they occur.无论插入和删除出现在列表中的哪个位置,插入和删除都很便宜。
  • It's cheap to combine lists with splicing.将列表与拼接结合起来很便宜。
  • You cannot randomly access elements, so getting at a particular element in the list can be expensive.您不能随机访问元素,因此获取列表中的特定元素可能会很昂贵。
  • Iterators remain valid even when you add or remove elements from the list.即使在列表中添加或删除元素,迭代器仍然有效。
  • If you need an array of the elements, you'll have to create a new one and add them all to it, since there is no underlying array.如果您需要一个元素数组,则必须创建一个新元素并将它们全部添加到其中,因为没有底层数组。

In general, use vector when you don't care what type of sequential container that you're using, but if you're doing many insertions or erasures to and from anywhere in the container other than the end, you're going to want to use list.通常,当您不关心使用的是哪种类型的顺序容器时,请使用 vector,但如果您在容器中的任何位置(而不是末尾)进行多次插入或擦除操作,则您会想要使用列表。 Or if you need random access, then you're going to want vector, not list.或者,如果您需要随机访问,那么您将需要向量,而不是列表。 Other than that, there are naturally instances where you're going to need one or the other based on your application, but in general, those are good guidelines.除此之外,在某些情况下,您自然会根据您的应用程序需要一个或另一个,但总的来说,这些都是很好的指导方针。

Situations where you want to insert a lot of items into anywhere but the end of a sequence repeatedly.您想要将大量项目重复插入到序列末尾以外的任何位置的情况。

Check out the complexity guarantees for each different type of container:查看每种不同类型容器的复杂性保证:

What are the complexity guarantees of the standard containers? 标准容器的复杂性保证是什么?

If you don't need to insert elements often then a vector will be more efficient.如果您不需要经常插入元素,那么向量会更有效。 It has much better CPU cache locality than a list.它比列表具有更好的 CPU 缓存局部性。 In other words, accessing one element makes it very likely that the next element is present in the cache and can be retrieved without having to read slow RAM.换句话说,访问一个元素使得下一个元素可能出现在缓存中,并且可以在无需读取慢速 RAM 的情况下检索。

Most answers here miss one important detail: what for?这里的大多数答案都忽略了一个重要细节:为什么?

What do you want to keep in the containter?您想在容器中保留什么?

If it is a collection of int s, then std::list will lose in every scenario, regardless if you can reallocate, you only remove from the front, etc. Lists are slower to traverse, every insertion costs you an interaction with the allocator.如果它是int的集合,那么std::list将在每个场景中丢失,无论您是否可以重新分配,您只能从前面删除等等。列表的遍历速度较慢,每次插入都会花费您与分配器的交互. It would be extremely hard to prepare an example, where list<int> beats vector<int> .准备一个例子是非常困难的,其中list<int>击败了vector<int> And even then, deque<int> may be better or close, not justyfing the use of lists, which will have greater memory overhead.即便如此, deque<int>可能更好或更接近,而不仅仅是使用列表,这将具有更大的内存开销。

However, if you are dealing with large, ugly blobs of data - and few of them - you don't want to overallocate when inserting, and copying due to reallocation would be a disaster - then you may, maybe, be better off with a list<UglyBlob> than vector<UglyBlob> .但是,如果您正在处理大而难看的数据块 - 而且很少 - 您不想在插入时过度分配,并且由于重新分配而复制将是一场灾难 - 那么您可能会更好list<UglyBlob>vector<UglyBlob>

Still, if you switch to vector<UglyBlob*> or even vector<shared_ptr<UglyBlob> > , again - list will lag behind.尽管如此,如果您切换到vector<UglyBlob*>甚至vector<shared_ptr<UglyBlob> > ,再次 - 列表将落后。

So, access pattern, target element count etc. still affects the comparison, but in my view - the elements size - cost of copying etc.因此,访问模式、目标元素计数等仍然会影响比较,但在我看来 - 元素大小 - 复制成本等。

One special capability of std::list is splicing (linking or moving part of or a whole list into a different list). std::list 的一项特殊功能是拼接(将部分或整个列表链接或移动到不同的列表中)。

Or perhaps if your contents are very expensive to copy.或者,如果您的内容复制起来非常昂贵。 In such a case it might be cheaper, for example, to sort the collection with a list.在这种情况下,例如,使用列表对集合进行排序可能会更便宜。

Also note that if the collection is small (and the contents are not particularly expensive to copy), a vector might still outperform a list, even if you insert and erase anywhere.另请注意,如果集合很小(并且复制内容不是特别昂贵),即使您在任何地方插入和擦除,向量仍可能优于列表。 A list allocates each node individually, and that might be much more costly than moving a few simple objects around.一个列表单独分配每个节点,这可能比移动几个简单的对象要昂贵得多。

I don't think there are very hard rules.我不认为有很硬的规则。 It depends on what you mostly want to do with the container, as well as on how large you expect the container to be and the contained type.这取决于您最想对容器做什么,以及您期望容器的大小和包含的类型。 A vector generally trumps a list, because it allocates its contents as a single contiguous block (it is basically a dynamically allocated array, and in most circumstances an array is the most efficient way to hold a bunch of things).向量通常胜过列表,因为它将其内容分配为单个连续块(它基本上是一个动态分配的数组,在大多数情况下,数组是保存一堆东西的最有效方式)。

Well the students of my class seems quite unable to explain to me when it is more effective to use vectors, but they look quite happy when advising me to use lists.好吧,我班的学生似乎无法向我解释什么时候使用向量更有效,但是当他们建议我使用列表时,他们看起来很高兴。

This is how I understand it我是这样理解的

Lists : Each item contains an address to the next or previous element, so with this feature, you can randomize the items, even if they aren't sorted, the order won't change: it's efficient if you memory is fragmented.列表:每个项目都包含一个指向下一个或上一个元素的地址,因此使用此功能,您可以将项目随机化,即使它们没有排序,顺序也不会改变:如果您的内存碎片化,这很有效。 But it also has an other very big advantage: you can easily insert/remove items, because the only thing you need to do is change some pointers.但它还有另一个非常大的优势:您可以轻松插入/删除项目,因为您唯一需要做的就是更改一些指针。 Drawback: To read a random single item, you have to jump from one item to another until you find the correct address.缺点:要读取随机单个项目,您必须从一个项目跳到另一个项目,直到找到正确的地址。

Vectors : When using vectors, the memory is much more organized like regular arrays: each n-th items is stored just after (n-1)th item and before (n+1)th item.向量:当使用向量时,内存组织得更像常规数组:每个第 n 项都存储在第 (n-1) 项之后和第 (n+1) 项之前。 Why is it better than list ?为什么它比 list 更好? Because it allow fast random access.因为它允许快速随机访问。 Here is how: if you know the size of an item in a vector, and if they are contiguous in memory, you can easily predict where the n-th item is;方法如下:如果您知道向量中某项的大小,并且它们在内存中是连续的,则可以轻松预测第 n 项的位置; you don't have to browse all the item of a list to read the one you want, with vector, you directly read it, with a list you can't.您不必浏览列表的所有项目来阅读您想要的内容,使用矢量,您可以直接阅读它,而使用列表则不能。 On the other hand, modify the vector array or change a value is much more slow.另一方面,修改向量数组或更改值要慢得多。

Lists are more appropriate to keep track of objects which can be added/removed in memory.列表更适合跟踪可以在内存中添加/删除的对象。 Vectors are more appropriate when you want to access an element from a big quantity of single items.当您想从大量单个项目中访问一个元素时,向量更合适。

I don't know how lists are optimized, but you have to know that if you want fast read access, you should use vectors, because how good the STL fasten lists, it won't be as fast in read-access than vector.我不知道列表是如何优化的,但你必须知道,如果你想要快速读取访问,你应该使用向量,因为 STL 固定列表有多好,它的读取访问速度不会比向量快。

Make it simple-简单点——
At the end of the day when you are confused choosing containers in C++ use this flow chart image ( Say thanks to me ) :-在一天结束时,当您在 C++ 中选择容器感到困惑时,请使用此流程图图像(感谢我):-

在此处输入图片说明

Vector-向量-

  1. vector is based on contagious memory载体基于传染性记忆
  2. vector is way to go for small data-set向量是小数据集的方法
  3. vector perform fastest while traversing on data-set向量在遍历数据集时执行速度最快
  4. vector insertion deletion is slow on huge data-set but fast for very small向量插入删除在庞大的数据集上很慢,但对于很小的数据集很快

List-列表-

  1. list is based on heap memory列表基于堆内存
  2. list is way to go for very huge data-set list 是获取非常大的数据集的方法
  3. list is comparatively slow on traversing small data-set but fast at huge data-set list 在遍历小数据集时相对较慢,但在遍历大数据集时速度较快
  4. list insertion deletion is fast on huge data-set but slow on smaller ones列表插入删除在庞大的数据集上很快,但在较小的数据集上很慢

任何时候都不能让迭代器失效。

Basically a vector is an array with automatic memory management.基本上,向量是一个具有自动内存管理功能的数组。 The data is contiguous in memory.数据在内存中是连续的。 Trying to insert data in the middle is a costly operation.尝试在中间插入数据是一项代价高昂的操作。

In a list the data is stored in unrelated memory locations.在列表中,数据存储在不相关的内存位置。 Inserting in the middle doesn't involve copying some of the data to make room for the new one.在中间插入并不涉及复制一些数据来为新数据腾出空间。

To answer more specifically your question I'll quote this page为了更具体地回答您的问题,我将引用此页面

vectors are generally the most efficient in time for accessing elements and to add or remove elements from the end of the sequence.向量通常是访问元素以及从序列末尾添加或删除元素的最有效时间。 For operations that involve inserting or removing elements at positions other than the end, they perform worse than deques and lists, and have less consistent iterators and references than lists.对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比双端队列和列表差,并且迭代器和引用的一致性不如列表。

When you have a lot of insertion or deletion in the middle of the sequence.当序列中间有很多插入或删除时。 eg a memory manager.例如内存管理器。

Preserving the validity of iterators is one reason to use a list.保持迭代器的有效性是使用列表的原因之一。 Another is when you don't want a vector to reallocate when pushing items.另一种情况是当您不希望在推送项目时重新分配向量。 This can be managed by an intelligent use of reserve(), but in some cases it might be easier or more feasible to just use a list.这可以通过智能使用 Reserve() 来管理,但在某些情况下,仅使用列表可能更容易或更可行。

When you want to move objects between containers, you can use list::splice .当你想在容器之间移动对象时,你可以使用list::splice

For example, a graph partitioning algorithm may have a constant number of objects recursively divided among an increasing number of containers.例如,图分区算法可能具有在数量不断增加的容器中递归划分的恒定数量的对象。 The objects should be initialized once and always remain at the same locations in memory.对象应该被初始化一次并且始终保持在内存中的相同位置。 It's much faster to rearrange them by relinking than by reallocating.通过重新链接重新排列它们比重新分配要快得多。

Edit: as libraries prepare to implement C++0x, the general case of splicing a subsequence into a list is becoming linear complexity with the length of the sequence.编辑:当库准备实现 C++0x 时,将子序列拼接成列表的一般情况随着序列的长度变得线性复杂。 This is because splice (now) needs to iterate over the sequence to count the number of elements in it.这是因为splice (现在)需要遍历序列以计算其中的元素数量。 (Because the list needs to record its size.) Simply counting and re-linking the list is still faster than any alternative, and splicing an entire list or a single element are special cases with constant complexity. (因为列表需要记录它的大小。)简单地计数和重新链接列表仍然比任何替代方法都快,并且拼接整个列表或单个元素是具有恒定复杂性的特殊情况。 But, if you have long sequences to splice, you might have to dig around for a better, old-fashioned, non-compliant container.但是,如果您有很长的序列要拼接,您可能不得不四处寻找更好的、老式的、不合规的容器。

In the case of a vector and list , the main differences that stick out to me are the following:vectorlist的情况下,我觉得主要区别如下:

vector向量

  • A vector stores its elements in contiguous memory.向量将其元素存储在连续的内存中。 Therefore, random access is possible inside a vector which means that accessing an element of a vector is very fast because we can simply multiply the base address with the item index to access that element.因此,在向量内部可以进行随机访问,这意味着访问向量的元素非常快,因为我们可以简单地将基地址与项目索引相乘以访问该元素。 In fact, it takes only O(1) or constant time for this purpose.事实上,为此只需要 O(1) 或常数时间。

  • Since a vector basically wraps an array, every time you insert an element into the vector (dynamic array), it has to resize itself by finding a new contiguous block of memory to accommodate the new elements which is time-costly.由于向量基本上包装了一个数组,因此每次将元素插入向量(动态数组)时,它都必须通过寻找新的连续内存块来调整自身大小以容纳新元素,这很费时间。

  • It does not consume extra memory to store any pointers to other elements within it.它不会消耗额外的内存来存储指向其中其他元素的任何指针。

list列表

  • A list stores its elements in non-contiguous memory.列表将其元素存储在非连续内存中。 Therefore, random access is not possible inside a list which means that to access its elements we have to use the pointers and traverse the list which is slower relative to vector.因此,在列表内部不可能进行随机访问,这意味着要访问其元素,我们必须使用指针并遍历相对于向量较慢的列表。 This takes O(n) or linear time which is slower than O(1).这需要 O(n) 或比 O(1) 慢的线性时间。

  • Since a list uses non-contiguous memory, the time taken to insert an element inside a list is a lot more efficient than in the case of its vector counterpart because reallocation of memory is avoided.由于列表使用非连续内存,因此在列表中插入元素所花费的时间比在其向量对应的情况下要高效得多,因为避免了内存的重新分配。

  • It consumes extra memory to store pointers to the element before and after a particular element.它消耗额外的内存来存储指向特定元素前后元素的指针。

So, keeping these differences in mind, we usually consider memory , frequent random access and insertion to decide the winner of vector vs list in a given scenario.因此,记住这些差异,我们通常会考虑内存、频繁的随机访问插入来决定给定场景中向量与列表的胜者。

The only hard rule where list must be used is where you need to distribute pointers to elements of the container.必须使用list的唯一硬性规则是您需要分发指向容器元素的指针。

Unlike with vector , you know that the memory of elements won't be reallocated.vector不同,您知道元素的内存不会被重新分配。 If it could be then you might have pointers to unused memory, which is at best a big no-no and at worst a SEGFAULT .如果可以,那么您可能有指向未使用内存的指针,这充其量是一个很大的SEGFAULT ,最坏的是SEGFAULT

(Technically a vector of *_ptr would also work but in that case you are emulating list so that's just semantics.) (从技术上讲, *_ptrvector也可以使用,但在这种情况下,您正在模拟list因此这只是语义。)

Other soft rules have to do with the possible performance issues of inserting elements into the middle of a container, whereupon list would be preferable.其他软规则与将元素插入容器中间可能出现的性能问题有关,因此list会更可取。

Lists are just a wrapper for a doubly-LinkedList in stl, thus offering feature you might expect from d-linklist namely O(1) insertion and deletion.列表只是 stl 中双重链接列表的包装器,因此提供了您可能期望从 d-linklist 中获得的功能,即 O(1) 插入和删除。 While vectors are contagious data sequence which works like a dynamic array.PS- easier to traverse.向量是具有传染性的数据序列,其工作方式类似于动态数组。PS-更易于遍历。

Summarizing the answers in a table for quick reference:总结表格中的答案以供快速参考:

Vector向量 List列表
Access使用权 Faster快点 Slower慢点
Insert/Delete Operations插入/删除操作 Slower慢点 Faster快点
Memory Allocation内存分配 Contiguous连续的 Non-contiguous不连续
Size Pre-allocation大小预分配 Need to be reserved需要预约 Not necessary to reserve不需要预约
Space Required Per Element每个元素所需的空间 Only for the element itself仅针对元素本身 For element and pointers to next对于元素和指向下一个的指针
(and optionally previous elements) (以及可选的先前元素)

List is Doubly Linked List so it is easy to insert and delete an element. List 是双向链表,因此很容易插入和删除元素。 We have to just change the few pointers, whereas in vector if we want to insert an element in the middle then each element after it has to shift by one index.我们只需要改变几个指针,而在 vector 中,如果我们想在中间插入一个元素,那么它后面的每个元素都必须移动一个索引。 Also if the size of the vector is full then it has to first increase its size.此外,如果向量的大小已满,则必须首先增加其大小。 So it is an expensive operation.所以这是一个昂贵的操作。 So wherever insertion and deletion operations are required to be performed more often in such a case list should be used.因此,在这种情况下,无论何时需要更频繁地执行插入和删除操作,都应该使用列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM