Sequential Containers in C++
Standard library containers are organized into families, and the family tells you whether elements are reached by position, by key, or through hashed lookup.
Deciding What Kind of Collection You Need
Before you write a program that stores a group of values, you have to decide what kind of collection you need. Should the items stay in the order you added them? Should you look an item up by a name or label? Should duplicates be allowed? The C++ standard library answers these questions with a set of ready-made container types, so you do not have to build each storage structure yourself. The library is designed around the idea that you pick the container that best fits the needs of your application.
This article walks through the containers by asking what each one is good at. The goal is to turn a list of names into a set of choices you can reason about. Keep three questions in mind as you read: does the container keep elements in a sequence, does it store elements under keys, and does it use hashed lookup?
Three Families of Standard Containers
The standard containers fall into families, and the family tells you how elements are arranged and how you reach them. Sequential containers keep elements in a sequence, so position is the main way to think about them. Associative containers store elements together with keys, so you reach a stored item by its key. Unordered containers are also keyed, but they are built on hash tables and are optimized for lookup, which is often done with a string key.
Read the diagram from top to bottom. The root is the idea of a standard container, and the three families sit beneath it. Each family then contains the specific containers the source names. The unordered family mirrors the associative family in its set-or-map and single-or-multiple distinctions, but its lookup mechanism is different. The rest of this article explains each family in turn, starting with the one whose elements are reached by position.
Tracing a Lookup by Position
Trace what happens when you want an element from a sequential container. A sequential container arranges its elements in a line, so you think about where an element sits in that line. Suppose the container holds four scores in order. To get the score that sits at position 2, you name that position. You do not search the values to find the one you want.
The highlighted cell is the one the lookup asks for. Its position, 2, identifies it. The values in the picture are invented for illustration. The source does not give example scores. The point of the picture is that a position-based lookup answers the question which place in the line, not which value.
Sequential Containers and Their Links
The source names four sequential containers, and each one differs in how it is built and which ends are easy to reach. The vector<T> is described as a variable-size vector, so it can grow or shrink as you add or remove elements. The list<T> is a doubly-linked list, so each element knows about the element before it and the element after it. The forward_list<T> is a singly-linked list, which links each element only to the next one. The deque<T> is a double-ended queue, so it is built for working at both of its ends.
| Container | Source description | What to notice |
|---|---|---|
| vector<T> | Variable-size vector | Grows or shrinks as elements are added or removed |
| list<T> | Doubly-linked list | Each element links to the element before it and the element after it |
| forward_list<T> | Singly-linked list | Each element links only to the next element |
| deque<T> | Double-ended queue | Built for working at both ends |
The four sequential containers named in the source, with the distinguishing detail from each description
The differences in linking and in the ends that are easy to reach are the reasons you would choose one over another. The source does not compare their speed or memory use, so this article does not make those claims. Choose by asking which kind of growth or end access your program needs.
second holds 75- scores is a sequential container, so its elements are reached by position
push_backadds each new value at the end of the line- scores[1] names position 1, which is the second element
Associative Containers Reached Through Keys
An associative container stores elements that are reached through keys. Instead of asking for the element at a position, you give the container a key and receive the stored item for that key. The source describes set<T> as a set that holds just a key with no separate value. The map<K,V> is an associative array that pairs a key of type K with a value of type V.
The keys and values in this picture are a generated illustration of the idea, not data taken from the source. The picture shows that you find a value by its key, and that the key itself is the handle you use to store and retrieve data.
anaAge holds 31- The map pairs each std::string key with an int value
- Writing ages["Ana"] stores a value under the key Ana
- Reading ages["Ana"] finds the value by its key, not by a position
The multi-versions relax one rule each. A multiset<T> lets a value occur many times, and a multimap<K,V> lets a key occur many times. So if the same key may appear more than once, a multimap is the family member the source points you to, while a plain map keeps one value per key.
Unordered Containers and Hashed Lookup
The unordered containers are optimized for lookup with a key, and the source notes that a key is often a string. They are implemented using hash tables. The source lists four of them: unordered_map<K,V>, unordered_multimap<K,V>, unordered_set<T>, and unordered_multiset<T>. Each one mirrors a container from the associative family, with the same set-or-map and single-or-multiple distinctions, but the lookup mechanism is a hashed one.
The point to hold onto is the purpose. When fast lookup by key is the main job, the unordered versions are the ones the source singles out. When you need the stored order or the sorted keys, the associative family is the one to consider, since the unordered family is described by its lookup mechanism rather than by ordering.
Container Adaptors and Container-Like Types
The standard library also provides container adaptors, named queue<T>, stack<T>, and priority_queue<T>. An adaptor is a class that provides a different interface to a container. It does not present the operations of a general-purpose collection. Instead, it presents a narrower set of operations suited to a particular access pattern. The source tells you the adaptors exist and gives their names, and it advises you to look them up when you need them.
Some types in the standard library behave like containers without fitting neatly into the three families or the adaptors. The source gives two examples. The array<T,N> is a fixed-size array, so its size is set as part of its type rather than growing as elements are added. The bitset<N> is a fixed-size collection of bits. Both are described as more specialized container-like types, and they are useful to know about so that you do not mistake them for the variable-size containers above.
| Kind | Examples named in the source | Defining idea |
|---|---|---|
| Ordinary containers | vector, list, forward_list, deque, set, map, and their unordered and multi versions | Store elements that you reach by position, key, or hashed key lookup |
| Container adaptors | queue, stack, priority_queue | Provide a different, narrower interface to a container |
| Container-like types | array, bitset | Behave like containers but are fixed-size and more specialized |
How ordinary containers differ from adaptors and container-like types
Tracing a Container Choice
Choosing a container from three questions
A program must store student names and look up a student's record by name. Duplicate names should not be needed, and the program does not need the names kept in any particular order. Which family fits best?
- 1Ask how the data is reached
The program looks up records by name, which is a key. A position-based sequential container is not the natural choice when the key is the main access path.
- 2Ask whether duplicates are allowed
The problem says duplicate names are not needed. That points to a set or map rather than a multiset or multimap, since those relax the one-occurrence rule.
- 3Ask whether ordering matters
The problem says the names need no particular order. The unordered family is the one the source singles out when fast lookup by key is the main job.
- 4Choose the container
An
unordered_map<K,V> with a string key fits. The record is the value V, and the name is the key K. This matches the source's note that unordered containers are often used with string keys.
An unordered_map from a string name to a record is the best fit, because lookup by key is the main job, duplicates are not needed, and no order is required.
Practice Choosing a Container
A program must keep the order in which customers arrive and must process the customer at the front of that line first. Which family or adaptor would you consider, and what question did you use to decide?
Hints
- Ask whether the main way to reach an element is by position or by key.
- Think about whether the program needs a narrower interface that offers only the operations for one access pattern.
- The queue<T> adaptor is one of the three adaptors the source names.
Name the family of each container: vector<T>, set<T>, and unordered_map<K,V>. Then say whether a multiset<T> would change the family of the set.
Hints
- vector is in the sequential family because it is reached by position.
- set and
unordered_mapare keyed, so compare their lookup mechanisms. - A multiset relaxes a rule about duplicates, not the family it belongs to.
Mistakes Learners Make with Container Families
Common mistake Treating every container as a list of values reached by position
What it looks likeLooking up a student's record in a vector by searching for the name, when the record is naturally reached by a name key
Why it failsSequential containers are arranged by position, while associative and unordered containers are reached through keys
Do this insteadAsk first whether the main access path is a position or a key, and pick the family that matches
Common mistake Assuming a multimap and a map differ in family
What it looks likeChoosing a map when the same key must appear several times
Why it failsThe multi-versions stay in the associative family and relax only the rule that a key occurs once
Do this insteadKeep the family and change only the duplicate rule, choosing multimap when keys repeat
Common mistake Assuming unordered containers behave exactly like associative containers
What it looks likeExpecting an unordered_map to keep its keys in a fixed order
Why it failsThe source describes unordered containers by their hashed lookup mechanism, not by ordering
Do this insteadDo not rely on ordering from an unordered container, and use the associative family when order matters
Common mistake Treating adaptors as general-purpose collections
What it looks likeExpecting a stack to offer every operation a vector offers
Why it failsAn adaptor provides a narrower interface suited to one access pattern
Do this insteadUse an adaptor for its access pattern and an ordinary container when you need general operations
Common mistake Mistaking array or bitset for a variable-size container, when their size is fixed as part of the type
Key Takeaways
- Standard containers are organized into sequential, associative, and unordered families, and the family tells you whether you reach elements by position, by key, or through hashed lookup.
- Sequential containers (vector, list,
forward_list, deque) are arranged in a line, so position is the main way to reach an element. - Associative containers (set, map, multiset, multimap) store elements with keys, and unordered containers use hash tables and are optimized for lookup by key.
- Container adaptors (queue, stack,
priority_queue) provide a narrower interface to a container, while array and bitset are specialized, fixed-size container-like types. - Choose a container by asking how elements are reached, whether duplicates are allowed, and whether ordering matters.