Concepts / Choosing the Right Container

Choosing the Right Container

Choosing a container is a performance decision that should match the data's usage.

  • Programming

Why the Container Decision Matters

When you write a program that stores a group of values, you have to pick a container to hold them. Many container types exist, and they are not interchangeable in every situation. Some suit sequences that are usually empty, and others suit sequences that grow large or change constantly. Choosing well means matching the container to how the data will actually be used, including the performance requirements of the program.

What do you think happens?

Your program keeps a list of pending items, but that list is usually empty and only occasionally gets a few entries. Which standard container does the source recommend you consider first?

  • vector

  • forward_list

  • unordered_map

  • queue

Reveal answer
Answer:

forward_list

The source recommends forward_list for sequences that are usually empty. The rest of this article explains why that guidance is tied to how the data is used, and what to do when the usage pattern does not settle the question.

Why Intuition Misleads

A very common approach is to pick a container based on what seems fast. You reason that a container is probably quick for a certain task, and you build the program around that feeling without checking. This is the misconception this article addresses: relying on intuition to decide how a container will perform.

The source is direct about this: intuition is not a reliable guide to performance, so you should measure instead. A feeling that one container is faster can be wrong for the operations your program really performs, and you will not discover that until you check. The safer habit is to treat your first guess as a candidate to test rather than as a decision.

Tracing a Choice Through Its Steps

The source supports a decision flow. You start from the expected usage of the data. A sequence that is usually empty points to forward_list. Where that expectation does not settle the question, measurement is the way to decide rather than intuition. The diagram below traces that path from a usage question to a measured choice, and the steps that follow explain each stage.

Usage questionHow is the data used?Expected usageWhat usage is expected?Usually empty?Does the expectation settleit?Choose forward_listSequence usually emptyMeasured choiceDecision confirmed byresultsMeasure candidatesRun the real operationsstartcompareyesnot settledconfirmdecide
What sequence of steps moves a developer from a data-usage question to a measured container choice?
  1. 1
    State the usage: write down how the data is used, such as whether the sequence is usually empty or grows large.
  2. 2
    Check the expectation: if the usage pattern points clearly to one container, such as forward_list for a sequence that is usually empty, that is your first candidate.
  3. 3
    Measure when the expectation does not settle it: run the operations your program really performs with each candidate container.
  4. 4
    Decide from the measured behavior, not from the impression you started with.

Sorting Containers into Families

Before you can choose among the standard containers, you need a way to think about them. 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 line, so position is the main way to think about them. Associative containers store elements together with keys. Unordered containers are also keyed, but they are built on hash tables and are optimized for lookup. The concept map below places the main families under the single idea of a standard container.

Standard containersSequentialReached by positionvectorAssociativeReached by keylistUnorderedHash table lookupforward_listdequeset, multisetmap, multimapunordered_set,unordered_multisetunordered_map,unordered_multimap
Which standard container types belong to each family, and how do the families relate to one another?

Sequential Containers by Position

A sequential container arranges its elements in a line, and you think about where an element sits in that line. The source names four examples. vector<T> is a variable-size vector, so it can grow or shrink as you add or remove elements. list<T> is a doubly-linked list, so each element knows about the element before it and the element after it. forward_list<T> is a singly-linked list, which links each element only to the next one. deque<T> is a double-ended queue, built for working at both of its ends. The differences in linking and in which ends are easy to reach are the reasons you would choose one over another.

The source's specific recommendation for forward_list is tied to usage. It recommends forward_list for sequences that are usually empty. Because the source links this recommendation to how often a sequence is expected to be empty, the question to ask is how the data is actually used, not which container has the most features.

Finding Data by Key Instead of Position

A key-based container differs from an order-based container in how you locate a stored item. In a sequential container, you think about the position of an element in the line. In an associative container, you reach a stored item by its key. The source describes set<T> as a set that holds just a key with no separate value, while map<K,V> is an associative array that pairs a key of type K with a value of type V. 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.

The diagram below shows how a map relates each key to its value. The names and values are a generated illustration of the idea, not data taken from the source. Notice that you do not ask for item number two; you ask for the item whose key is a given name.

"name"key"Ada"value"city"key"London"value"role"key"engineer"valuemaps tomaps tomaps to
When I need to find or store an element, how does a key-based container locate it differently from an order-based container that uses position?

Unordered Containers and Hash Tables

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 mirrors a container from the associative family, with the same set-or-map and single-or-multiple distinctions, but its lookup mechanism is hashed.

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. Because they are described by their lookup mechanism rather than by their ordering, do not expect them to behave like the associative containers in every respect. The source does not describe the internal details of the hash tables, so this article does not describe them either.

Adaptors and Container-Like Types

Definition

A container adaptor, such as queue<T>, stack<T>, or priority_queue<T>, 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.

Container-like types are a separate group. The source gives array<T,N>, a fixed-size array whose size is set as part of its type rather than growing as elements are added, and bitset<N>, a fixed-size collection of bits. Both are more specialized than the variable-size containers above. Knowing them helps you avoid mistaking them for the containers that grow and shrink.

Walking Through a Container Choice

Worked example

Choosing a container for a usually empty sequence

A program keeps a sequence of pending tasks. Most of the time the sequence is empty, and occasionally a few tasks are added. Which container should the developer choose, and how should they confirm the choice?

  1. 1
    State the usage

    The sequence is usually empty. This is the usage pattern that the source ties to forward_list.

  2. 2
    Apply the expected-usage rule

    The source recommends forward_list for sequences that are usually empty, so forward_list is the first candidate.

  3. 3
    Check whether the expectation settles it

    If the program's real operations are simple and the usage is clearly the stated pattern, the expectation may settle the choice. If the developer is unsure how the operations behave, the question is not settled.

  4. 4
    Measure when unsettled

    Run the operations the program really performs with forward_list and with one other candidate, then compare the observed behavior. The measurement, not a sense of speed, makes the final decision.

Result

The developer starts with forward_list because the sequence is usually empty, and uses measurement to confirm the choice whenever the usage pattern alone does not settle it.

Mistakes When Choosing a Container

  • Common mistake

    Picking a container because it sounds fast

    What it looks like

    Choosing a container for a task only because it feels quick, then building the whole program around that feeling.

    Why it fails

    The source states that intuition is not a reliable guide to performance.

    Do this instead

    Treat the first guess as a candidate and measure the operations your program really performs.

  • Common mistake

    Ignoring how often a sequence is empty

    What it looks like

    Using a general container for a sequence that is usually empty without considering the usage pattern.

    Why it fails

    The source ties its forward_list recommendation to sequences that are usually empty, so the usage pattern is part of the decision.

    Do this instead

    Write down the expected usage first, then check whether it points to forward_list.

  • Common mistake

    Treating a key-based container as if it were ordered by position

    What it looks like

    Expecting to fetch the second stored item of a map by its position number.

    Why it fails

    Associative containers store elements with keys, so you reach a stored item by its key.

    Do this instead

    Access the item through its key, and use a sequential container when position is what matters.

  • Common mistake

    Assuming unordered containers behave exactly like associative ones

    What it looks like

    Expecting an unordered_map to keep elements in the same order as a map.

    Why it fails

    The source describes unordered containers by their hash-table lookup mechanism, not by their ordering.

    Do this instead

    Rely only on the stated purpose, lookup by key, and check any other behavior you need.

  • Common mistake

    Confusing adaptors and container-like types with ordinary containers

    What it looks like

    Expecting a stack<T> to offer every operation of a vector<T>.

    Why it fails

    An adaptor provides a narrower interface suited to a particular access pattern, and array and bitset are specialized fixed-size types.

    Do this instead

    Check which family or kind a type belongs to before assuming what operations it offers.

Practice Applying Choose Then Measure

MEDIUM

A program stores a list of log messages that is usually empty, and during a busy period it gains many messages quickly. Walk through the choose-then-measure approach. Which container do you start with, and what would you measure before deciding?

Hints
  • Start with the expected usage. Is the sequence usually empty?
  • Ask whether the usage pattern alone settles the question, or whether the busy periods make it unsettled.
  • Measure the operations the program really performs, such as adding messages during a busy period, rather than relying on impression.
EASY

Name one family for each of these needs: looking up a stored value by a text label, keeping a line of items reached by position, and storing a collection where each key is unique. Justify each choice using only how the family organizes its elements.

Hints
  • Keyed lookup points to the associative or unordered families.
  • Position-based access points to the sequential family.
  • Think about whether the keyed container is hashed or not, using the family definitions.

Checking the Choice

Choosing a container is a performance decision that should match how the data is used. Start from the expected usage, including whether a sequence is usually empty, then move to measurement when that expectation does not settle the question. Use the family to reason about access: position for sequential containers, keys for associative and unordered containers, and specialized types only when their narrower role fits.

Key Takeaways

  • Choosing a container is a performance decision that should match the data's usage.
  • Intuition is not a reliable guide to performance, so measure the operations your program really performs.
  • The source recommends forward_list for sequences that are usually empty, and measurement decides when usage alone does not settle the choice.
  • Sequential containers are reached by position, associative containers by key, and unordered containers use hash-table lookup by key.
  • Container adaptors and array or bitset are narrower or specialized types, not ordinary variable-size containers.

Related concepts

More concepts starting with C