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
- Capacity
- Modifiers
- 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.
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
mapin which duplicate keys are allowed.- unordered_map
A
mapof unique key-value pairs stored based on the key object hash function. Added in C++11.- unordered_multimap
An
unordered_mapin which duplicate keys are allowed.
More to Explore
Red-black tree on Wikipedia