Learn C++ STL

Lesson 1 of 9 · map and multimap: A Key Finds Its Value

Module 9 · map and multimap: A Key Finds Its Value

map: Each Key Holds a Value, and the Keys Stay Sorted

FreeReading

In this lesson

  • Declare a std::map, store a value under a key with m[k] = v, count with m[k]++, and walk every pair back in key order.
  • Ask whether a key is there with count and find without adding it, read a found value through it->second, and erase a key.
  • Explain what a map is in one sentence, spot the trap of reading m[k] for a missing key, and pick a multimap for repeated keys.

Kenji's café gives a loyalty stamp for every visit. At each visit, the counter asks how many stamps this customer has now. At closing time, Kenji wants every customer and their stamps, in alphabetical order. A vector of (name, stamps) pairs answers each question with a scan of the whole list, Module 2's find. This lesson gives Kenji a container that does both jobs while the customers walk in.

A key and its value

A map is a container of pairs, like Module 1's pair. The first part of each pair is its key, what you look things up by: here, a customer's name. The second part is its value, what the key holds: here, a number of stamps. A map holds each key at most once, and it keeps its pairs sorted by key.

An array can answer "how many stamps?" too, but only by a number: stamps[3], never stamps["maria"]. A map's key can be a name, a word or any type that < can compare. Here are Kenji's stamps after seven visits, drawn twice.

Kenji's stamps as a sorted table and as a tree with a pair in every node Kenji's stamps after seven visits: one map, two pictures A walk gives a sorted table: key value amara bob david kenji maria zara 1 2 1 1 1 1 Stored: a tree with a pair in every node: maria 1 bob 2 zara 1 amara 1 kenji 1 david 1 The tree is ordered by the key alone; each value rides along in its key's node.

Both pictures hold the same six pairs. The table is what a walk through the map gives back. The tree is how the map stores them, and the next section opens it. So stamps["bob"] names Bob's value, 2, and Bob's pair sits between Amara's and David's in key order.

Inside: Module 8's tree, with a pair in every node

Module 8 kept a set in a balanced binary search tree, where a search walks down one level at a time. A map is that same tree. The only change is what a node holds: a key and its value together, as one pair. The tree is ordered by the key alone, and the value rides along.

The C++ standard names no tree. It promises only O(log n) steps to find, add or erase a key, where n is the number of keys. GCC's library, libstdc++, keeps a red-black tree, as it does for a set. The pictures in this module leave the colours out. So the names stay sorted because the tree keeps them sorted, not because anything sorts them at the end.

Watch the stamps arrive

Here is Kenji's first morning, one call per visit: maria, bob, zara, kenji, bob again, amara and david. Then the counter looks up zara and carol with find, and finally reads carol with []. Every picture is the real shape GCC 12's library built, printed from a run on Compiler Explorer. The values are in the notes and in the table under the widget.

Ten steps on one map<string, int> stamps. Each tree is written level by level from the root down, a slash between two levels, each pair as its key and value.

CallThe pairs, level by levelWhat happened
stamps["maria"]++maria 1maria is the first key, so her node becomes the root, with 1 stamp.
stamps["bob"]++maria 1 / bob 1bob comes before maria, so his node hangs on her left, with 1 stamp.
stamps["zara"]++maria 1 / bob 1, zara 1zara comes after maria, so her node hangs on the right, with 1 stamp.
stamps["kenji"]++maria 1 / bob 1, zara 1 / kenji 1kenji comes before maria and after bob, so his node hangs right of bob.
stamps["bob"]++maria 1 / bob 2, zara 1 / kenji 1The walk compares maria, then finds bob. No new node: bob now has 2 stamps.
stamps["amara"]++maria 1 / bob 2, zara 1 / amara 1, kenji 1amara comes before maria and before bob, so her node hangs left of bob.
stamps["david"]++maria 1 / bob 2, zara 1 / amara 1, kenji 1 / david 1david goes left at maria, right at bob and left at kenji. Six customers, 4 levels.
stamps.find("zara")maria 1 / bob 2, zara 1 / amara 1, kenji 1 / david 1Found after 2 comparisons: zara has 1 stamp. find changes nothing.
stamps.find("carol")maria 1 / bob 2, zara 1 / amara 1, kenji 1 / david 1carol would sit left of david, and that spot is empty: not found. find returns end(), and nothing changes.
stamps["carol"]maria 1 / bob 2, zara 1 / amara 1, david 1 / carol 0, kenji 1A read with [] walks the same way, then adds carol with 0 stamps, and the tree turns so david moves up. Seven keys now, one more than before the read.

Bob's second visit walked down to his node and only raised his count. find("carol") fell off the tree below david and changed nothing. The last step is this lesson's trap: a read with [] added carol with 0 stamps, and the tree turned to make room. So a map grows only when a new key is stored, and [] stores one even when you only meant to read.

The syntax you need on day one

std::map and std::multimap, and their everyday calls

#include <map>

map<K, V> m;                      an empty map from K keys to V values
map<K, V> m{{k1, v1}, {k2, v2}};  a map made from a list of pairs
m[k] = v;                         store v under k, or overwrite k's value
m[k]++;                           add 1 to k's value; a missing k starts at 0
m.count(k)                        1 if k is there, 0 if not; adds nothing
m.find(k)                         an iterator to k's pair, or m.end()
it->first   it->second            the key and the value of that pair
m.erase(k);                       remove k and its value, if k is there
m.size()                          how many keys
for (const auto& [k, v] : m)      every pair, in key order

multimap<K, V> mm;                the same, but a key may repeat; no []
  • map<K, V>: two types in the angle brackets, the key's and the value's. The header <map> brings both map and multimap.
  • m[k] finds k, or first adds k with a zero value, and then hands you k's value to read or change. It never fails, and that is its trap.
  • count and find only look. find returns an iterator, the marker for one place that Module 2 used, and m.end() means "not found".
  • A walk gives each pair with .first and .second, or, with Module 1's structured binding, as two names at once.

So everyday map code names a key every time, and the map decides where its pair lives.

m[k] stores a value, and a missing key starts at zero

m[k] = v stores v under k. When k is new, the map first adds it with the value type's zero, written V() in C++. That zero is 0 for an int and an empty string for a string. Then the = writes over the zero. m[k]++ and m[k] += 2 work the same way, so counting needs no setup.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps;
    stamps["maria"] = 3;
    stamps["bob"]++;
    stamps["bob"]++;
    stamps["maria"] += 2;
    cout << "maria " << stamps["maria"] << ", bob " << stamps["bob"] << '\n';

    map<string, string> note;
    note["kenji"] += "tea";
    note["kenji"] += ", no sugar";
    cout << "kenji: " << note["kenji"] << '\n';
    cout << stamps.size() << " customers, " << note.size() << " note\n";
    return 0;
}
maria 5, bob 2
kenji: tea, no sugar
2 customers, 1 note

Bob's first ++ found no bob, so the map added him at 0 and raised him to 1. Kenji's note started as an empty string, and += added the words to it. Two customers and one note: every key was added once, however often it was touched. So m[k]++ is a whole counting program, with no check and no starting value.

count and find: look without adding

m.count(k) says how many pairs have the key k, which on a map is 1 or 0. m.find(k) returns an iterator to k's pair, or m.end() when k is missing. Through that iterator, it->first is the key and it->second the value. Neither call ever adds a key.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps{{"bob", 2}, {"maria", 5}, {"zara", 1}};
    cout << stamps.count("zara") << ' ' << stamps.count("kenji") << '\n';

    auto it = stamps.find("maria");
    if (it != stamps.end()) {
        cout << it->first << " has " << it->second << " stamps\n";
    }
    if (stamps.find("kenji") == stamps.end()) {
        cout << "kenji is not a customer yet\n";
    }
    cout << "still " << stamps.size() << " customers\n";
    return 0;
}
1 0
maria has 5 stamps
kenji is not a customer yet
still 3 customers

The map still holds 3 customers after asking about kenji twice. Compare find's answer with end() before you read it->second; lesson 02 shows what reading end() printed in one run. So use count for a plain yes or no, and find when you also want the value.

The trap: reading m[k] adds k

m[k] cannot tell a read from a write. When k is missing, it adds k with a zero value first, even inside a cout. So a map you only read can still grow.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps{{"bob", 2}, {"maria", 5}};
    cout << "size before: " << stamps.size() << '\n';
    cout << "carol has " << stamps["carol"] << " stamps\n";
    cout << "size after:  " << stamps.size() << '\n';
    cout << "count(\"carol\") is now " << stamps.count("carol") << '\n';
    return 0;
}
size before: 2
carol has 0 stamps
size after:  3
count("carol") is now 1

One read printed a believable 0 and added carol for good. The size went from 2 to 3, and count now finds her. Kenji's closing list would show a customer who never came. So when you only want to ask, ask with count or find.

The walk in key order

A range-for walks a map from the smallest key to the largest, one pair at a time. Each pair has two parts: p.first, the key, and p.second, the value. Module 1's structured binding names both parts at once, so [name, count] reads better than .first and .second.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps;
    stamps["zara"] = 1;
    stamps["amara"] = 4;
    stamps["kenji"] = 2;

    for (const auto& p : stamps) {
        cout << p.first << ": " << p.second << '\n';
    }

    cout << "again:";
    for (const auto& [name, count] : stamps) {
        cout << ' ' << name << '=' << count;
    }
    cout << '\n';
    return 0;
}
amara: 4
kenji: 2
zara: 1
again: amara=4 kenji=2 zara=1

The keys went in as zara, amara and kenji and came out in alphabetical order, in both loops. const auto& reads each pair where it is, without a copy. So every walk of a map is a walk in key order.

erase by key

m.erase(k) removes k together with its value, and returns how many pairs it removed. On a map that is 1 or 0. Erasing a key that is not there does nothing, with no error.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps{{"bob", 2}, {"maria", 5}, {"zara", 1}};
    cout << "erase(\"bob\") removed " << stamps.erase("bob") << '\n';
    cout << "erase(\"carol\") removed " << stamps.erase("carol") << '\n';
    for (const auto& [name, count] : stamps) {
        cout << name << ' ' << count << '\n';
    }
    return 0;
}
erase("bob") removed 1
erase("carol") removed 0
maria 5
zara 1

Bob's pair is gone, and the walk no longer meets him. The 0 for carol says she was never there. So erasing a missing key is safe, and its answer tells you whether anything went.

Each key once

A map never holds two pairs with the same key. A second m[k] = v finds the first pair and overwrites its value. So the newest write wins.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, string> phone;
    phone["maria"] = "555-0101";
    phone["bob"] = "555-0102";
    phone["maria"] = "555-0188";
    cout << phone.size() << " names\n";
    for (const auto& [name, number] : phone) {
        cout << name << ": " << number << '\n';
    }
    return 0;
}
2 names
bob: 555-0102
maria: 555-0188

Maria's old number is gone, and the map still holds two names. So a map suits any job where each key has one current value, like a phone number that changed.

multimap: the cousin that keeps repeated keys

Sometimes one key needs several values: Bob has a home number and a work number. A multimap keeps every pair, repeated keys included, still sorted by key. It lives in the same header and has the same insert, count, find and erase. Its count(k) says how many pairs have the key k.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    multimap<string, string> phones;
    phones.insert({"bob", "555-0102"});
    phones.insert({"maria", "555-0101"});
    phones.insert({"bob", "555-0177"});
    for (const auto& [name, number] : phones) {
        cout << name << ": " << number << '\n';
    }
    cout << "bob has " << phones.count("bob") << " numbers, " << phones.size() << " in all\n";
    return 0;
}
bob: 555-0102
bob: 555-0177
maria: 555-0101
bob has 2 numbers, 3 in all

Both of Bob's numbers stayed, side by side, in the order they arrived. A multimap has no [], because phones["bob"] could not know which of Bob's numbers you mean.

multimap<string, string> phones;
phones["bob"] = "555-0102";

GCC 12 stops at the runner's flags with error: no match for 'operator[]' (operand types are 'std::multimap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >' and 'const char [4]'). Lesson 02 shows equal_range, which hands you every value of one key. So reach for a multimap when a key needs several values, and for a map when one is enough.

Example 1: fruit prices in key order

The smallest map program: four prices go in, in any order, and come out sorted by fruit.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> price;
    price["mango"] = 120;
    price["banana"] = 60;
    price["apple"] = 180;
    price["guava"] = 80;

    cout << price.size() << " fruits on the board:\n";
    for (const auto& [fruit, taka] : price) {
        cout << fruit << ' ' << taka << '\n';
    }
    return 0;
}
4 fruits on the board:
apple 180
banana 60
guava 80
mango 120

Nobody sorted anything: the walk follows the tree, so the board comes out alphabetical. Four [] writes made four pairs, one per fruit.

Run in Compiler
Example 2: Kenji's stamps for one morning

The seven visits from the widget, counted with []. The counter checks Bob with find, then the program prints the closing list.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps;
    for (string name : {"maria", "bob", "zara", "kenji", "bob", "amara", "david"}) {
        stamps[name]++;
    }

    auto it = stamps.find("bob");
    if (it != stamps.end()) {
        cout << "bob has " << it->second << " stamps\n";
    }

    cout << "closing list:\n";
    for (const auto& [name, count] : stamps) {
        cout << "  " << name << ' ' << count << '\n';
    }
    return 0;
}
bob has 2 stamps
closing list:
  amara 1
  bob 2
  david 1
  kenji 1
  maria 1
  zara 1

The counts match the widget's picture before carol: bob 2, everyone else 1. The question at the counter used find, so asking added nobody. With a million customers, each visit would still take a few dozen steps.

Run in Compiler
Example 3: Zara's book of calling codes

Zara plans a trip and keeps a map from country to its telephone calling code. She looks up three countries, and one of them is not in her book.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> calling_code{
        {"bangladesh", 880}, {"brazil", 55}, {"japan", 81},
        {"kenya", 254}, {"peru", 51}, {"spain", 34}};

    for (string country : {"japan", "chile", "kenya"}) {
        auto it = calling_code.find(country);
        if (it != calling_code.end()) {
            cout << country << ": +" << it->second << '\n';
        } else {
            cout << country << ": not found\n";
        }
    }
    cout << calling_code.size() << " countries in the book\n";
    return 0;
}
japan: +81
chile: not found
kenya: +254
6 countries in the book

Chile was missing, so find returned end() and the program said so. The book still holds 6 countries: no lookup added one. The map was built from a brace list of pairs, one pair per country.

Run in Compiler
Example 4: Amara's contacts, a map against a multimap

Amara copies the business cards on her desk into two contact lists. Bob gave her two cards with different numbers.

#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
using namespace std;

int main() {
    vector<pair<string, string>> cards{
        {"bob", "555-0102"}, {"zara", "555-0144"}, {"bob", "555-0177"}, {"amara", "555-0110"}};

    map<string, string> latest;
    multimap<string, string> every;
    for (const auto& [name, number] : cards) {
        latest[name] = number;
        every.insert({name, number});
    }

    cout << "map keeps " << latest.size() << ":\n";
    for (const auto& [name, number] : latest) {
        cout << "  " << name << ' ' << number << '\n';
    }
    cout << "multimap keeps " << every.size() << ":\n";
    for (const auto& [name, number] : every) {
        cout << "  " << name << ' ' << number << '\n';
    }
    return 0;
}
map keeps 3:
  amara 555-0110
  bob 555-0177
  zara 555-0144
multimap keeps 4:
  amara 555-0110
  bob 555-0102
  bob 555-0177
  zara 555-0144

The map kept one number per name, and for Bob it kept the last one, because his second latest[name] = number overwrote the first. The multimap kept all four cards. Amara would write exactly this when she is not sure every name has one number.

Run in Compiler

Where this is used

  • Java's TreeMap. Java's sorted map is a red-black tree, the same structure libstdc++ uses. Its documentation promises log(n) time for containsKey, get, put and remove, the promise C++ makes for a map's lookups, inserts and erases.
  • SQLite's tables. SQLite stores every ordinary table on disk as a "table b-tree", ordered by a 64-bit integer key: the row's rowid. Finding a row by its rowid is a walk down that tree, the same idea as a map's find.
  • LevelDB's memtable. LevelDB, a key-value store library, keeps its data sorted by key. Each new write is appended to a log, then put into an in-memory table, the memtable, which is a skip list ordered by key. When it has grown large enough, it is written to disk as a sorted table. A skip list is not a tree, but it keeps the map's promise: walk it, and the keys come out in order.

Common mistakes

1. Checking for a key with [].

map<string, int> stamps{{"bob", 2}, {"maria", 5}};
if (stamps["zara"] == 0) {
    cout << "zara has no stamps yet\n";
}
for (const auto& [name, count] : stamps) {
    cout << name << ' ' << count << '\n';
}

No message at the runner's flags. It printed zara has no stamps yet, then bob 2, maria 5 and zara 0: the check itself added Zara, so the closing list names a customer who never came. Ask with stamps.count("zara") == 0 instead. You will write the [] version because it reads exactly like the question.

2. Forgetting the header.

#include <iostream>
#include <string>
using namespace std;

int main() {
    map<string, int> stamps;
    stamps["maria"]++;
    cout << stamps.size() << '\n';
    return 0;
}

An error at the runner's flags: error: 'map' was not declared in this scope, then GCC 12's own fix: note: 'std::map' is defined in header '<map>'; did you forget to '#include <map>'?. A multimap needs the same header. You will forget it because <string> is already at the top, and the map was added later.

3. Changing a key through an iterator.

map<int, int> guests{{12, 4}, {15, 2}};
auto it = guests.find(12);
it->first = 13;

An error at the runner's flags: error: assignment of read-only member 'std::pair<const int, int>::first'. Kenji wanted to move a party from table 12 to table 13. But the key decides where the pair sits in the tree, so the map hands every key out as const: read-only. Erase the old key and store the value under the new one; lesson 02 shows a way that keeps the node.

4. Expecting the order you added.

map<string, int> course;
course["soup"] = 1;
course["rice"] = 2;
course["sweet"] = 3;
for (const auto& [dish, number] : course) {
    cout << dish << '\n';
}

No message, and it prints rice, soup and sweet: alphabetical, not the order of the meal. A map keeps no record of the order its keys arrived in. Keep a vector of pairs when that order matters. You will expect it back because a vector always kept it.

Brain teaser

Zara counts the letters of a word with a map whose keys are chars.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<char, int> letters;
    string word = "banana";
    for (char c : word) {
        letters[c]++;
    }
    for (const auto& [c, n] : letters) {
        cout << c << ' ' << n << '\n';
    }
    return 0;
}

What does the program print, line by line? Say the order of the letters, and why it is that order, before you run it.

A char key is compared by its code. Look up the codes of 'a', 'b' and 'n' in the ASCII table.

Exercise 1Easy

David runs the school canteen. His price list names every item and its price in taka, and no two items share a name. All day, students hand in orders: an item and how many they want. For each order David needs its cost, and at closing time the day's total.

Input. A line with n, then n lines, each an item and its price. Then a line with m, then m lines, each an item and a quantity. Every ordered item is on the price list.

Output. One line per order, in the order they came: its cost, the price times the quantity. Then one more line: the total of all the orders.

Constraints. 1 <= n, m <= 30000. An item is 1 to 10 lowercase letters. 1 <= price <= 100000 and 1 <= quantity <= 1000. The total can pass 2147483647.

Sample. Input 4, then samosa 15, chai 10, cake 40 and singara 12, then 5, then chai 2, cake 1, samosa 3, chai 1 and singara 4 gives 20, 40, 45, 10, 48 and 163. The last line is the day's total.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n = 0;
    cin >> n;
    map<string, int> price;   // item name -> its price in taka
    for (int i = 0; i < n; i++) {
        string item;
        int taka = 0;
        cin >> item >> taka;
        // Remember that item costs taka.
    }

    int m = 0;
    cin >> m;
    long long total = 0;   // the day's total can pass 2147483647
    for (int i = 0; i < m; i++) {
        string item;
        int qty = 0;
        cin >> item >> qty;
        // Print the cost of this order, and add it to total.
    }

    // Print the day's total.

    return 0;
}

Graded as price-lookup, a free problem in this module's problem set. The hidden tests include one item with one order, one item ordered 1000 times, and names that start other names. They also include 30000 orders at the largest price and quantity, a total of 3000000000000.

Run in Compiler
Exercise 2Medium

Maria's class sat the same quiz twice, a week apart. Some students missed one of the two rounds. The teacher wants every student who sat both rounds, with both scores, to see who improved.

Input. A line with n, then n lines from the first round, each a name and a score. Then a line with m, then m lines from the second round.

Output. One line per student who sat both rounds, in alphabetical order: the name, the first score and the second score. If nobody sat both, print the word none.

Constraints. 1 <= n, m <= 100000. A name is 1 to 10 lowercase letters, and no name appears twice in one round. Every score is a whole number from 0 to 100.

Sample. Input 4, bob 70, maria 85, zara 60, kenji 90, then 3, zara 75, maria 80 and alice 66 gives maria 85 80 and zara 60 75. Bob and Kenji missed the second round, and Alice missed the first.

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    int n;
    cin >> n;
    map<string, int> first;   // name -> score in the first round
    for (int i = 0; i < n; i++) {
        string name;
        int score;
        cin >> name >> score;
        first[name] = score;
    }
    int m;
    cin >> m;
    map<string, int> second;   // name -> score in the second round
    for (int i = 0; i < m; i++) {
        string name;
        int score;
        cin >> name >> score;
        second[name] = score;
    }

    // Walk first in key order. For every name that second also holds,
    // print the name and both scores. Print none if nobody sat both.

    return 0;
}

Not graded on its own. Walk one map in key order and let find on the other answer for each name; the walk already gives the alphabetical order.

Run in Compiler
Exercise 3Hard

At the end of the month, Kenji gives a prize to the customer with the most stamps. When several customers tie for the most, each of them gets a prize. His notebook has one name per visit.

Input. A line with n, then n names, one per visit, separated by spaces or new lines.

Output. Two lines. First the most stamps any customer has. Then every customer with that many stamps, in alphabetical order, separated by single spaces.

Constraints. 1 <= n <= 100000. A name is 1 to 10 lowercase letters. Count with the map, then walk it only once.

Sample. Input 7 and bob zara bob maria zara kenji amara gives 2, then bob zara. Bob and Zara came twice each.

#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    map<string, int> stamps;
    for (int i = 0; i < n; i++) {
        string name;
        cin >> name;
        stamps[name]++;
    }

    // Walk stamps once. Keep the most stamps seen so far, and the names
    // that have that many. Then print the most stamps on one line, and
    // those names on the next, separated by single spaces.

    return 0;
}

Not graded on its own. The idea the lesson only pointed at: the walk meets the names in alphabetical order. Keep the names that share the best count so far, and start the list again when a bigger count turns up.

Run in Compiler

Common doubts

  • Why are my keys sorted when I added them in another order?

    Each new key goes to its sorted place in the tree, and the order you added them in is not stored anywhere. If you need that order later, keep the keys in a vector as well.

  • Can a map hold two equal keys?

    No. A second m[k] = v overwrites the first value. A multimap keeps both pairs, and its count says how many there are.

  • What value does a key I never set have?

    None: the key is not in the map. Read it with [], though, and the map adds it with the value type's zero, 0 for an int and an empty string for a string. count and find never add it.

  • Is a map slow?

    A lookup walks one path down the tree, one comparison per level. A million keys need at least 20 levels, and libstdc++ keeps the tree at most about twice that tall, as Module 8 showed. A scan of a vector of a million pairs may take a million steps.

  • When do I use a map, and when a set?

    A set when the only question is "is it there?". A map when each key carries a value, such as a count, a price or a phone number.

Key takeaways

  • A map holds key-value pairs, each key at most once, and walks them back sorted by key, whatever order they came in.
  • m[k] = v stores or overwrites, and m[k]++ counts; a missing key starts at its zero, 0 for an int and an empty string for a string.
  • count and find look without adding; it->first is the key and it->second the value, after you check find's answer against end().
  • Reading m[k] for a missing key adds it: the size grows, and the closing list names someone who never came.
  • Inside is Module 8's balanced tree with a pair in every node, ordered by the key; a multimap keeps repeated keys and has no [].
  • Go deeper: Under the Hood, a pair in every node and what a miss costs (Pro).

Next, lesson 02 goes through every map and multimap operation, one by one, each with its cost.

End of lesson 1

Mark it done, and your progress moves with you.

Next: Every map and multimap Operation, One by One, With Its Cost