15.7. The map class

A map refers to any data structure that 'maps' keys to values. The map class is arguably the most popular container in the STL after vector.

All the containers discussed so far focused on storing 1 thing. That is, each stores values of a single type. Maps add a new wrinkle. A map stores pairs of things. Traditionally, the pair stored is referred to as a key-value pair.[1]

Nearly every programming language provides some kind of map implementation. Some languages use the terms associative array or dictionary List, but structurally, they are very similar.

Values are retrieved from a map using the key. Each key must be unique. In other words, keys are members of a set. Like a std::set, adding a second node with the same key replaces the old value. Unlike a std::set, a std::map provides the map::operator[].

 1#include <iostream>
 2#include <map>
 3#include <string>
 4
 5int main() {
 6  std::map<std::string, int> name_counts {{"Alice", 27},
 7                                          {"Bob", 3},
 8                                          {"Clara", 1}};
 9
10  for (const auto& kvp : name_counts) {
11    std::cout << kvp.first << ": "
12              << kvp.second << '\n';
13  }
14  name_counts["Bob"]   = 42;      // update existing value
15  name_counts["Darla"] = 9;       // insert a new value
16
17  // get map values
18  std::cout << "Alice is " << name_counts.find("Alice")->second << '\n';
19  // or get the key iterator, then print
20  auto it = name_counts.find("Alice");
21  std::cout << "Alice is " << it->second << '\n';
22
23  std::cout << "Bob is " << name_counts.at("Bob") << '\n';
24  std::cout << "Darla is " << name_counts["Darla"] << '\n';
25}

15.7.1. Selected map functions

Access and assignment

operator=, at and operator[]

Capacity

empty, size, and max_size

Modifiers

clear, emplace, insert, erase, swap

Lookup

count, find, equal_range, upper_bound and lower_bound

For large data sets, the lookup functions in a map are faster than their counterparts in a sequential container such as vector.

Note

There is no push_back() for a map.

The map decides where elements go, not you. All access requires either knowing the key or having an iterator.

15.7.2. Map structure

Internally, a map is a sorted complete binary tree. (Technically it is often implemented as a Red-black tree). Each node in the tree is a std::pair.

A complete binary tree

All nodes are sorted by their keys. Sorting is managed using operator< by default, but this can be configured in the map constructor using a custom compare function or class, just as with a set.

 1#include <iostream>
 2#include <map>
 3#include <set>
 4#include <string>
 5
 6using std::string;
 7
 8void print (std::set<string> keys) {
 9  for (const auto& key: keys) {
10    std::cout << key << ' ';
11  }
12}
13
14int main() {
15  std::map<string, int> inventory {
16    {"apple", 12},
17    {"kiwi", 4},
18    {"lemon", 1},
19    {"pear", 4},
20    {"peach", 4},
21    {"grape", 100},
22    {"cocoa", 3},
23  };
24
25  std::set<string> inventory_keys;
26
27  // extract keys from the inventory map
28  for (const auto& i: inventory) {
29    auto result = inventory_keys.insert(i.first);
30    if (!result.second) std::cout << "no insertion\n";
31  }
32
33  std::cout << "All fruit keys:\n";
34  print (inventory_keys);
35
36  std::set<string> keys;
37  auto it = inventory.upper_bound("kiwi");
38  while(it != inventory.end()) {
39    auto result = keys.insert(it->first);
40    if (!result.second) std::cout << "no insertion\n";
41    ++it;
42  }
43  std::cout << "\n\nAll fruit keys greater than 'kiwi':\n";
44  print (keys);
45
46}

Using a customer comparator, we can store map items in reverse order:

 1#include <functional>  // provides std::greater
 2#include <iostream>
 3#include <map>
 4#include <string>
 5
 6using std::string;
 7
 8// print inventories with different custom comparators
 9template <class Comparator>
10void print (const string title, const std::map<string, int, Comparator>& x) {
11   std::cout << title;
12   for (const auto& kvp: x) {
13     std::cout << kvp.first << ", " << kvp.second << '\n';
14   }
15}
16
17int main() {
18  std::map<string, int> inventory {
19     {"apple", 12},
20     {"kiwi", 4},
21     {"lemon", 1},
22     {"pear", 4},
23     {"peach", 4},
24     {"grape", 100},
25     {"cocoa", 3},
26  };
27
28  print ("Initial inventory:\n", inventory);
29
30
31  // define a reverse ordered map
32  // a lambda is not the best choice here
33  const auto greater_than = [] (string lhs, string rhs) { return lhs > rhs;};
34  std::map<string, int, decltype(greater_than)> reverse_inventory1 (greater_than);
35
36  // but it works
37  for (auto& i: inventory) {
38    reverse_inventory1.insert(i);
39  }
40  print ("\n\nReverse inventory using lambda:\n", reverse_inventory1);
41
42
43  // STL provides many basic operations wrapped in a std::function
44  std::map<string, int, std::greater<string>> reverse_inventory2;
45  for (auto& i: inventory) {
46    reverse_inventory2.insert(i);
47  }
48  print ("\nReverse inventory using std::greater:\n", reverse_inventory2);
49
50  return 0;
51}

15.7.3. Variations on std::map

The STL provides 3 alternate forms of map class:

multimap

A map in which duplicate keys are allowed.

unordered_map

A map of unique key-value pairs stored based on the key object hash function. Added in C++11.

unordered_multimap

An unordered_map in which duplicate keys are allowed.


More to Explore