您什么时候更喜欢使用 std::list而不是 std::vector

发布于 2024-10-18 09:36:06 字数 431 浏览 7 评论 0原文

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

所以我的问题是,

  • 您到底什么时候更喜欢 std::list 而不是 std::vector?到底为什么?
  • 您什么时候更喜欢 std::vector 而不是 std::list?为什么?

如果有性能方面的考虑,那么也请列出它们并附上详细的解释/信息。

如果可能的话,也引用一些参考文献来支持你的答案。

I've never used std::list<T> myself. I was wondering when people use it when we already have std::vector<T> which is just like arrays with contiguous memory. std::vector seems like a perfect choice when we need sequential container!

So my question is

  • When exactly do you prefer std::list over std::vector? and why exactly?
  • When do you prefer std::vector over std::list? and why?

If there is performance consideration, then please list them too with detail explanation/information.

If possible, quote some references also, to support your answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

策马西风 2024-10-25 09:36:06

所以我的问题是:您到底什么时候更喜欢 std::list 而不是 std::vector

当我需要在性能敏感区域使用顺序容器并且分析显示std::list速度更快时。

到目前为止,这种情况还没有发生在我身上。

(当我必须存储中间有大量插入/删除的非常大的对象时,我可能会想首先尝试 std::list 。但是,在实践中,我从未遇到过这样的情况一个用例。)

So my question is: when exactly do you prefer std::list over std::vector?

When I need a sequential container in a performance-sensitive area and profiling shows std::list is faster.

So far, this has never happened to me.

(I might be tempted to try std::list first when I would have to store very big objects with lots of insertion/removal in the middle. However, in practice, I've never come across such a use-case.)

我很OK 2024-10-25 09:36:06

列表更适合在中间的任何位置插入或删除,向量更适合在末尾插入。

向量也更适合访问元素。

这是它们实施方式的产物。

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

如果更改的数量很大(与访问相比)并且它们不在末尾,我会使用列表。

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

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

至于参考文献,最新的 C++0x 草案(在这个答案时)部分说明了( 23.3.4,列出):

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

第 23.3.5 节(关于向量):

向量是支持随机访问迭代器的序列容器。此外,它还支持末尾(摊销)恒定时间插入和擦除操作;中间的插入和擦除需要线性时间。

Lists are better for inserting or deleting anywhere in the middle, vectors are better for inserting at the end.

Vectors are also better for accessing elements.

This is an artefact of the way they're implemented.

So, if a collection changes very little (compared to accesses) or the changes are concentrated at the end, I'd use a vector.

If the number of changes is substantial (compared to accesses) and they're not at the ends, I'd use a list.

By way of example, reading in a collection at program start-up and hardly ever changing it (or if the changes are only adding to the end), this would be a good candidate for a vector.

On the other hand, a phone book application for a particularly popular and fickle rock star, I'd be looking towards a list. Actually I'd be looking toward a database connection but that was the best example I could come up with at short notice :-)

As to references, the latest C++0x draft (at the time of this answer) states in part (23.3.4, lists):

A list is a sequence container that supports bidirectional iterators and allows constant time insert and erase operations anywhere within the sequence, with storage management handled automatically. Unlike vectors and deques, fast random access to list elements is not supported.

Section 23.3.5 (on vectors):

A vector is a sequence container that supports random access iterators. In addition, it supports (amortized) constant time insert and erase operations at the end; insert and erase in the middle take linear time.

蓦然回首 2024-10-25 09:36:06

std::liststd::vector 之间进行选择时需要考虑一些权衡。

另外,std::list 与连续内存无关,如果您无法承受迭代器失效或者需要在开始/中间/结束中插入分摊常量时间,那么它会非常有用。

There are a few trade-offs to be considered when choosing between std::list and std::vector.

Also std::list is not about contiguous memory, it can be quite useful if you can't afford iterator invalidation or if you need amortized constant time insertion in the begin/middle/end.

爱情眠于流年 2024-10-25 09:36:06

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

The only (few) times I preferred std::list is due to the list::splice member function. If you are shuffling around subranges within a list or between lists this operation can be significantly faster than using std::vector.

晨曦慕雪 2024-10-25 09:36:06

我不需要重复基础知识,但我通过艰难的方式学到的是,如果插入性能相关并且您有“大”对象,那么您应该真正考虑 std::list, 即使如果您只在末尾插入。 (嗯,一个列表,或者可能是一个智能指针/ ptr_vector 的向量。)

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

在未知计数插入情况下,std::vector 的问题是:

  • 由于向量总是会过度分配,因此对于典型实现来说,最坏情况下您要付出 50% 的空间开销(单个字符串向量,其中例如,对象有 24 字节(MSVC),给定区区 100,000 个元素,可能会产生 2MB 的空间开销,并且对于具有更多字符串或其他较大成员的较大对象,它会成倍增加。)
  • 每次向量必须重新分配时,它都必须复制周围的所有对象,如果你有稍微复杂的东西,那可不便宜。显然,移动语义会有所帮助,但如果对象本身很大,重复的复制可能仍然相关。如果在向量构建过程中多次复制所有对象,则平均摊销插入时间(最后)理论上是否恒定并不重要。您可能会注意到这一性能的提升。
  • 对象变大的速度非常快:只有两个空 std::string 的结构在 MSVC 上已经具有不可移动 48 字节。

这并不是为了抨击 std::vector,我仍然默认使用它——但知道你的数据结构会发生什么!

I don't need to repeat the basics, but what I learned the hard way is that if insertion performance is relevant and you have "large" objects, then you should really consider a std::list, even if you're only inserting at the end. (Well, a list or possibly a vector of smart pointer / ptr_vector.)

We had some use cases where we had to build up collections of a priori unknown size of structs of multiple small std::string and using a std::vector totally killed insertion performance and added a non-neglible memory overhead.

The problem with std::vector in the unknown count insert scenario is:

  • Since the vector will always over allocate, you pay 50% space overhead worst case for typical implementations (A single vector of string, where an object has, e.g. 24 bytes (MSVC), given meagre 100,000 elements, could have a space overhead of 2MB. And it will multiply for larger objects with more string or other biggish members.)
  • Every time the vector has to reallocate, it has to copy all the objects around, and that ain't cheap if you have anything slighly complex. Obviously, move-semantics will help, but if the objects themselves are large, the repeated copy may still be relevant. It doesn't matter if the average amortized insertion time (at the end) is theoretically constant, if you copy all your objects muliple times during building up of the vector. You may notice this peformance hit.
  • Objects get large really quick: A struct of only two empty std::string already has non-moveable 48 bytes on MSVC.

This is not to bash std::vector, I still use it by default -- but know what to expect of your datastructures!

泡沫很甜 2024-10-25 09:36:06

除了其他答案之外,
基于节点的容器(list/关联容器)可以提供强大的
例外保证。
即使容器变异操作(例如插入)抛出
例外,所有指向元素的指针/引用/迭代器都保留
有效。
然而,线性内存(分段连续内存单元)容器可以
只提供基本保障。
当插入抛出异常时,即使插入实际上并未执行,
指针/引用/迭代器可能会失效
(尽管容器本身可以安全地销毁)。

In addition to other answers,
node-base containers(list/associative containers) can provide strong
exception guarantee.
Even if a container-mutating operation(for example insertion) throws an
exception, all the pointers/references/iterators to the elements remain
valid.
However, linear-memory(piecewise contiguous memory cell) containers can
provide only basic guarantee.
When an insertion throws, even if the insertion isn't actually executed,
pointers/references/iterators may be invalidated
(though the container itself can be destructed safely).

眼角的笑意。 2024-10-25 09:36:06

有人会说,可以说,您应该 永远不要再在代码中使用链表。 ;)

一个问题是向量具有更好的引用局部性,并且在许多情况下,由此产生的巨大性能增益将超过诸如删除之类的更(算法上)高效操作的优势并插入到一个链表中。

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

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

但请注意,即使您确实执行了大量此类操作,最好将列表“托管”在 std::vector 的连续缓冲区中。

我在这篇博文中更详细地讨论了这一点,并提供了一些图表和代码示例: http:// upcoder.com/12/vector-hosted-lists/

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

Some would say that you should, arguably, never ever ever use linked list in your code again. ;)

One issue is that vectors have much better locality of reference, and the big performance gains resulting from this will then, in many cases, outweigh the advantages of more (algorithmically) efficient operations for things like delete and insert in a linked list.

(See the blog post lined above for more discussion of this specific issue, with benchmarks and so on.)

So, often a std::vector will outperform std::list, even if you are doing a certain number of operations for which a list would be more natural (e.g. arbitrary element deletion, insert at arbitrary position, or splice).

But note that, even when you do do lots of these kinds of operations, you may be better off 'hosting' your list within the contiguous buffer of a std::vector.

I discuss this in more detail in this blog post, with some diagrams and code examples: http://upcoder.com/12/vector-hosted-lists/

In the specific case when you need to delete arbitrary elements, but don't need to insert at arbitrary positions or splice, a good alternative to a full blown linked list can be to just mark entries as 'dead' and skip over these dead entries during iteration.

绮筵 2024-10-25 09:36:06

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

列表可以在 O(1) 的任何位置插入/删除/拼接,并且这不会使任何迭代器失效。除了末尾之外,向量对于插入/擦除来说是 O(n) 的,即使这样也仅在大小 < 时才用于插入。容量;矢量无法拼接。性能比这更微妙,例如另一个答案中提到的缓存位置。

Use a list when its invalidation semantics and performance characteristics match your requirements.

List can insert/erase/splice anywhere in O(1), and this doesn't invalidate any iterators. Vector is O(n) for insert/erase except at the end, and even then only for insert if size < capacity; vector can't splice. Performance is even more subtle than this, with the caching locality mentioned in another answer, for example.

瑕疵 2024-10-25 09:36:06

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

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

std::list is my preferred almost exclusively for but one property that vector does not share, and that's knowing my pointers into a list will always be valid. Because of this, I can use this to contain all the instances of whatever asset I need in a single area, while also loaning out references to other objects to use. Best example I could think of would be an image loader that only keeps one copy of pixels in memory, while loaning out pointers to multiple entities so they can all draw from it.

All vector has going for it is O(1) access time. While that sounds like a huge asset, in practice I have very rarely ever needed to access items in a data structure outside stepping from 'first' to 'last'

带上头具痛哭 2024-10-25 09:36:06

当您需要频繁修改除前面或后面以外的其他地方的序列时,可以使用 std::list 。与 std::list 相比,std::vector 中此类操作的开销很大。

You use std::list when you need to frequently modify the sequence in other places than the front or back. The overhead of such operations is large in std::vector in comparision std::list.

何以笙箫默 2024-10-25 09:36:06

当您进行大量删除/插入操作时,应该使用列表。

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

You should use a list when you're doing a lot of deletions / insertions.

Vector can be used if the total size of elements does not change a lot, and if you do some swapping.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文