Concepts / Associative Containers and Maps in C++

Associative Containers and Maps 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.

  • Programming

Choosing a Container Before You Store Anything

Before you write a program that stores a collection 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 items 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.

What do you think happens?

You want to store a list of phone numbers and find any number by typing the person's name. Which family of container should you reach for: sequential, or one that is reached by a key?

  • Sequential, because you find items by their position

  • A keyed family (associative or unordered), because you find items by a key

Reveal answer
Answer:

A keyed family, either associative or unordered. You look up a phone number by the name, which is a key, so a container whose elements are reached by keys fits the job.

The source says that associative containers store elements together with keys, so you reach a stored item by its key. It also says unordered containers are keyed and are optimized for lookup, often with a string key.

Three Families Based on How You Reach Elements

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.

Standard containersSequentialvector, list, forward_list,dequeAssociativeset, map, multiset,multimapUnorderedunordered_map,unordered_set, andmulti-versionsContainer adaptorsqueue, stack,priority_queueContainer-like typesarray, bitsetfamilyfamilyfamilyseparate categoryseparate category
Which containers belong to which family? The three families sit under the single idea of a standard container, and the adaptors and container-like types sit beside them as separate categories.

Tracing a Key-Value Pair in a Map

The source describes map<K,V> as an associative array that pairs a key of type K with a value of type V. Each stored entry is therefore a pair: one key and the value attached to it. To trace what a map holds, follow the entries one at a time and ask, for each key, what value it is paired with. The keys and values in the picture below are a generated illustration of the idea, not data taken from the source.

Ana31lookup: BenBen27Cara45matches key
How is each key paired with its value, and what does a lookup return? Each name is a key, its age is the stored value, and a lookup by a name returns the value paired with that key.
Worked example

Looking up a value by its key

A map pairs each name (a key) with an age (a value). Trace what a lookup with the key Ben returns.

  1. 1
    Identify the key

    The lookup key is Ben. In an associative container you reach a stored item by its key, not by its position.

  2. 2
    Find the matching entry

    Among the stored entries, the one whose key is Ben is the entry that matches the lookup.

  3. 3
    Read the paired value

    The map pairs the key Ben with the value 27, so that is the value the lookup gives back.

Result

The lookup with the key Ben returns 27, the value paired with that key.

Position Versus Key Lookup

The main difference between families is what you use to find an element. In a sequential container you think about where an element sits in the line. In an associative or unordered container you give the container a key and ask for the element that carries it. The same question, which item do I want, is answered by a different piece of information in each case.

Positionvector, list, forward_list,dequeKeyset, map, multiset,multimapKey through a hashtableunordered_map,unordered_set, andmulti-versions
Which information does each family use to reach a stored element? Sequential containers are reached by position, associative containers by key, and unordered containers by a key that is turned into a hashed lookup.

Notice that the unordered family is also keyed. What sets it apart is the lookup mechanism, since the source states that these containers are implemented using hash tables. The source gives the stated purpose, which is fast lookup by key, and it does not describe the internal details of those hash tables, so this article does not describe them either.

Sequential Containers and 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, and the differences between them come from how they are linked and which ends are easy to reach.

  • 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, so it is built for working at both of its ends.

Associative and Unordered Containers

Both of the keyed families come in set and map forms, and in multi-versions that relax one rule each. Choosing among them is a question of whether you need a key with a value, a key alone, or permission for repeats. The two subsections below separate the associative family from the unordered family, because the source treats their purpose and mechanism differently.

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, gives their names, and advises you to look them up when you need them.

Some types behave like containers without fitting neatly into the three families or the adaptors. 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. Knowing about these prevents you from mistaking them for the variable-size containers described earlier.

Using a Map in a Small Program

The example below shows the key-value idea in code. A std::map<std::string, int> pairs each string key with an int value, and the lookup uses the key rather than a position. This snippet is an illustrative example written for this article, not code from the source.

cpp
Notes on the code
  • The key type is std::string and the value type is int, matching map<K,V> with K and V.
  • Each assignment stores one key with its value.
  • The lookup ages["Ben"] reaches the stored value by its key, not by a position.

Mistakes Beginners Make With Container Families

  • Common mistake

    Looking up a sequential container by a name

    What it looks like

    Trying to find the age of Ben in a vector by treating the name as a position

    Why it fails

    Sequential containers are reached by position, so a name is not the lookup information they use.

    Do this instead

    Use a keyed container such as a map when the name is the thing you search by.

  • Common mistake

    Assuming every keyed container works the same way

    Why it fails

    The source defines the associative family by stored keys and the unordered family by hash tables, so the lookup mechanisms differ.

    Do this instead

    Choose the family by the lookup pattern and check the behavior you need.

  • Common mistake

    Treating a container adaptor as a general-purpose collection

    Why it fails

    An adaptor presents a narrower set of operations suited to a particular access pattern.

    Do this instead

    Use an adaptor such as stack or queue when that narrow pattern is what you need, and consult its reference for its operations.

  • Common mistake

    Expecting array to grow like vector

    Why it fails

    The source says array<T,N> has its size set as part of its type rather than growing as elements are added.

    Do this instead

    Use vector when you need a variable-size collection.

Practice Choosing a Family

EASY

For each need, name the family you would choose and say whether it is reached by position, by key, or by hashed key lookup: (a) keep a running list of tasks in the order they were added, (b) find a product price by its code, (c) a fast lookup of usernames where lookup speed is the main job, (d) a fixed-size array of ten readings.

Hints
  • Ask whether the element is reached by where it sits in a line or by a key.
  • The source singles out the unordered versions when fast lookup by key is the main job.
  • Fixed size belongs to the container-like types, not the variable-size containers.

Key Takeaways

  • The standard containers fall into sequential, associative, and unordered families, and the family tells you whether elements are reached by position, by key, or through hashed lookup.
  • Sequential containers such as vector, list, forward_list, and deque are arranged by position, while associative containers store elements with keys and map<K,V> pairs each key with a value.
  • Unordered containers are keyed, often by a string, and are implemented using hash tables, which is why the source singles them out for fast lookup by key.
  • Container adaptors such as queue, stack, and priority_queue offer a narrower interface to an underlying container, while array and bitset are specialized, fixed-size container-like types.

Related concepts

More concepts starting with A