tags:

views:

282

answers:

10

Hi

I noticed in Effective STL that

vector is the type of sequence that should be used by default.

What's does it mean? It seems that ignore the efficiency vector can do anything. Could anybody offer me a scenario that vector is not a feasible option but list must be used?

Thanks!

+1  A: 

When you have a lot of insertion or deletion in the middle of the sequence. e.g. a memory manager.

AraK
so the difference between them is just efficiency, not about functional issue.
skydoor
They both model a *sequence* of elements of course. There is a small difference in usage, as mentioned by @dirkgently but you have to look at the complexity of your "often done" operations to decide which sequence to choose(@Martin answer).
AraK
@skydoor - There are a few functional differences. For example, only vector supports random-access (i.e., can be indexed).
Manuel
@skydoor: efficiency translates to performance. Poor performance can ruin functionality. Performance is the advantage of C++, after all.
Potatoswatter
+9  A: 

Situations where you want to insert a lot of items into the middle of a sequence repeatadly.

Check out the complexity gurantees for each different types of container:

http://stackoverflow.com/questions/181693/what-are-the-complexity-guarantees-of-the-standard-containers

Martin York
Inserting elements at the end also counts because it can lead to memory allocation and element copying costs. And also, inserting elenets at the begining of a vector is next to impossible, `list` has `push_front`
Notinlist
No, inserting elements at the end of a vector is amortised constant time. The memory allocations only happen on occasionally, and you can preallocate the vector to prevent it. Of course, if you *MUST* have guaranteed consistent constant time insertions, I guess this is still an issue.
Brian
@Notinlist - Is the following "next to impossible" for you? v.insert(v.begin(), i)
Manuel
in what application we have to insert a lot of items into the middle of a sequence repeatedly?
skydoor
@Manuel: Yes, it can be commanded in 20 chars or something, but where it leads to? Depending on implementation it can mean the copy of all elements every time. The easily usable slots (between size() and capacity()) are at the end, not the begining.
Notinlist
@Notinlist - I agree with you, it's just that I didn't want the OP to think that the interface is not there in case one wants to shoot himself in the (performance) foot.
Manuel
@skydoor: If the ordering is somehow important it might be desired, and you know the ordering beforehand. However, if you are inserting data that is not ordered, and not near-ordered a binary search tree is a better solution as the structure itself maintains the ordering, it also provides much quicker lookups than a (linked) list (as long as the above mentioned cases are not true.) If the data inserted into the tree is ordered or near ordered it more or less degenerates into a linked list and nothing is gained. That can be avoided by using a self-balancing tree, but they are more complicated.
Skurmedel
@skydoor: And hashmaps, they don't maintain order though but usually have very fast lookup times (faster than binary search trees)
Skurmedel
@skydoor: A word processor.
jmucchiello
@Skurmedel: Not necessarily faster than BSTs; algorithmic complexity in the form of big-O notation is concerned with categories rather than actual speed. See various answers/comments at http://stackoverflow.com/questions/2196995/is-there-any-advantage-of-using-map-over-unordered-map-in-case-of-trivial-keys
Roger Pate
@Roger Pate: Good point, thanks. And if you need to write the structure yourself I would expect a BST to be simpler to do right.
Skurmedel
+2  A: 

Any time you cannot have iterators invalidated.

dirkgently
But never jump to that conclusion about iterators without asking whether persistent *references* into a `deque` would suffice.
Potatoswatter
+1  A: 

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. 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.

Hans Passant
+1  A: 

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.

John Dibling
+1  A: 

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.

f4
+6  A: 

One special capability of std::list is splicing (linking or moving part of or a whole list into a different 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).

UncleBens
+1. Splicing is over overlooked, but unfortunately isn't constant-time as desired. :(( (It can't be if list::size is constant-time.)
Roger Pate
I'm quite sure list::size is (allowed to be) linear for this very reason.
UncleBens
@Roger: `list::size` is not necessarily constant time. See http://stackoverflow.com/questions/228908/is-listsize-really-on and http://gcc.gnu.org/ml/libstdc++/2005-11/msg00219.html
Potatoswatter
@Potatoswatter: That the standard is vague and that you consequently can't rely on "compliant" implementations makes it even more of a problem. You literally have to avoid the stdlib to get a portable and dependable guarantee.
Roger Pate
@Roger: yes, unfortunately. My current project strongly relies on splice operations and that structure is almost straight C. Even more unfortunately, in N3000 sequence `splice` between different lists is specified as linear complexity and `size` is specifically constant. So, to accommodate novices who iterate on `size` or whatever, a whole class of algorithms is out of reach for the STL, or any "compliant" containers period.
Potatoswatter
+2  A: 

When you want to move objects between containers, you can use 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. This is because splice (now) needs to iterate over the sequence to count the number of elements in it. (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.

Potatoswatter
+4  A: 

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).
  • Erasures at the end of the vector are constant time, but for the rest it's 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. 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.

Jonathan M Davis
Also, allocating from the free store is not free. :) Adding new items to a vector performs O(log n) free store allocations, but you can call `reserve()` to reduce that to O(1). Adding new items to a list (i.e. not splicing them) performs O(n) free store allocations.
bk1e
Another consideration is that `list` frees memory when you erase elements, but `vector` does not. A `vector` does not reduce its capacity when you reduce its size, unless you use the `swap()` trick.
bk1e
@bk1e: I really want to know your trick in reserve() and swap() :)
nXqd
@nXqd: if you need to add N elements to a vector, call v.reserve(v.size()+N) so that it performs only one free store allocation. The swap() trick is here: http://stackoverflow.com/questions/253157/how-to-downsize-stdvector
bk1e
@bk1e: Thanks so much :)
nXqd
A: 

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. Why is it better than 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; 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.

gokoon