C++ Course
STL Β· Lesson 42
Maps
A map is a container that stores pairs β every key has a value, and the keys always stay in sorted order. Think of a phone book: you look up a name (the key) and you get the number (the value). In C++, that is std::map.
1. What Is a Map?
A map is an STL container that stores keyβvalue pairs. Each key is unique, and the map keeps all its keys sorted from smallest to largest. You never have to sort it yourself β the map does that for you every time you add something.
Maps are perfect whenever you need to look something up by name: a student's marks by their name, a product's price by its code, or a word's meaning by the word itself. Behind the scenes a map is usually built as a balanced tree, so finding, adding and removing a key are all fast β about as fast as searching a phone book by flipping it open in the middle, again and again.
To use a map, include the <map> header and write std::map<KeyType, ValueType>. For example, std::map<std::string, int> is a map from words (strings) to whole numbers. Each entry inside is one std::pair β the key in .first and the value in .second.
2. Your First Map
Let us build a small map of names and exam scores. We add three pairs with square brackets, then print the scores one by one. Watch the output order β the map prints the names in alphabetical order, even though we added them in a different order.
Code story: We create a map called scores that links a student's name to their marks. We store three students, then ask the map for Alice's, Bob's and Charlie's marks and print them. Notice that the printed names come out sorted, not in the order we typed them.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> scores;
scores["Charlie"] = 76;
scores["Alice"] = 95;
scores["Bob"] = 88;
cout << "Alice: " << scores["Alice"] << endl;
cout << "Bob: " << scores["Bob"] << endl;
cout << "Charlie: " << scores["Charlie"] << endl;
return 0;
}Alice: 95 Bob: 88 Charlie: 76
What just happened? The map stored three nameβscore pairs. When we asked for scores["Alice"], the map jumped straight to Alice's entry and gave back 95. The keys stayed sorted inside the map, so printing in key order gives Alice, Bob, Charlie.
3. Inserting and Accessing Values
There are two everyday ways to put values into a map. The square-bracket way β m[key] = value β is short and simple, and it also overwrites a value if the key already exists. The insert() way adds a pair only if the key is new, and it will not overwrite an old value.
To read safely, use at(): it returns the value for a key, and if the key is missing it throws an error instead of silently creating a new entry. That difference matters β operator[] on a missing key quietly inserts it with a default value (0 for numbers, empty for strings), which can surprise you.
Code story: We keep a map of subject names and marks. First we add Math with 90 and Science with 85, then we try to change Math to 95 using insert β it stays 90 because insert never overwrites. Finally we use at() to read Science safely and print the map's size.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> marks;
marks["Math"] = 90;
marks["Science"] = 85;
marks.insert({"Math", 95}); // ignored: Math is already there
cout << "Math: " << marks.at("Math") << endl;
cout << "Science: " << marks.at("Science") << endl;
cout << "Size: " << marks.size() << endl;
return 0;
}Math: 90 Science: 85 Size: 2
What just happened? Square brackets added Math and Science. insert() tried to set Math to 95 but did nothing because the key already existed. at() read the values safely, and size() told us the map holds exactly 2 pairs.
Here is the whole everyday toolbox at a glance:
| Operation | What it does | Example | Result |
|---|---|---|---|
m["Math"] = 95 | Adds the pair, or overwrites the value if the key is already there | m["Math"] = 95 | Value stored |
m.insert({"Math", 95}) | Adds the pair only if the key is new; never overwrites | m.insert({"Math", 95}) | Added, or nothing changes |
m.at("Math") | Returns the value; throws an error if the key is missing | m.at("Math") | 95 |
m.find("Math") | Returns an iterator to the pair, or end() if the key is missing | m.find("Math") | Iterator or end() |
m.count("Math") | Counts how many times the key appears: 0 or 1 | m.count("Math") | 0 or 1 |
4. Finding and Counting Keys
Before you read a value, it is often smart to check whether the key is even there. The find() function returns an iterator to the pair if the key exists, or end() if it does not. The count() function is simpler: it returns 1 if the key exists and 0 if it does not, because a map never holds the same key twice.
Checking first is the safe habit. It keeps you from accidentally creating new entries with operator[] and from reading a value that was never stored.
Code story: A small shop keeps fruit prices in a map. We check whether "banana" and "mango" are in the shop's list. For banana we find the pair and print its price; for mango we confirm it is missing. Then we print the map's size.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> prices;
prices["apple"] = 50;
prices["banana"] = 60;
cout << "Has banana: " << prices.count("banana") << endl;
cout << "Has mango: " << prices.count("mango") << endl;
auto it = prices.find("banana");
if (it != prices.end()) {
cout << "Price of banana: " << it->second << endl;
}
cout << "Size: " << prices.size() << endl;
return 0;
}Has banana: 1 Has mango: 0 Price of banana: 60 Size: 2
What just happened? count("banana") returned 1 because banana is in the map, while count("mango") returned 0. find() gave us an iterator to banana's pair, and it->second fetched the price 60. The map still holds only 2 pairs.
5. Searching by Position: lower_bound and upper_bound
Sometimes you do not want one exact key β you want the first key that is at least a certain value, or the first key after it. That is what lower_bound() and upper_bound() are for. lower_bound(k) returns an iterator to the first key that is not less than k β so it lands on k itself if k is present. upper_bound(k) returns an iterator to the first key greater than k.
These two functions are how you answer questions like "what is the first score of 30 or more?" or "how many keys are below 40?" β range questions that a plain find() cannot answer.
Code story: The map holds number words for the keys 10, 20, 30, 40 and 50. lower_bound(30) jumps to the first key that is 30 or more β that is 30 itself. upper_bound(30) jumps past 30 to the next key, 40. Then we count all the keys smaller than 40 by hand: 10, 20 and 30.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<int, string> codes;
codes[10] = "ten";
codes[20] = "twenty";
codes[30] = "thirty";
codes[40] = "forty";
codes[50] = "fifty";
auto low = codes.lower_bound(30);
auto high = codes.upper_bound(30);
cout << "From 30: " << low->second << endl;
cout << "After 30: " << high->second << endl;
int below = 0;
for (auto it = codes.begin(); it != codes.end(); ++it) {
if (it->first < 40) below++;
}
cout << "Count below 40: " << below << endl;
return 0;
}From 30: thirty After 30: forty Count below 40: 3
What just happened? lower_bound(30) landed on the pair 30 β "thirty", and upper_bound(30) landed one step further, on 40 β "forty". The little loop counted the keys 10, 20 and 30 β three keys below 40.
6. Iterating Over a Map
To visit every pair, use a range-based for loop with auto&. Each step hands you one pair: .first is the key and .second is the value. Because a map is always sorted, the loop visits the pairs in key order β names in alphabetical order, numbers from smallest to largest.
Use auto& (with the &) so you work with the real pair inside the map and not a copy of it. If you only need to read, const auto& is the cleanest choice.
Code story: We store the ages of three friends in a map. Then we walk through the whole map with a range-based for loop. Each step hands us one pair: .first is the friend's name and .second is their age. Watch the printed order β it is alphabetical, because the map always keeps its keys sorted.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> ages;
ages["Ravi"] = 25;
ages["Maya"] = 22;
ages["Arjun"] = 28;
for (auto& pair : ages) {
cout << pair.first << " is " << pair.second << endl;
}
return 0;
}Arjun is 28 Maya is 22 Ravi is 25
What just happened? The loop visited Arjun, Maya and Ravi β in that order β even though Ravi was added first. Each pair gave us its key through .first and its value through .second.
7. Removing Entries
Entries leave a map in two ways. erase(key) removes one pair β the one with that key β and returns how many pairs it removed (0 or 1). clear() empties the whole map at once. Both are handy when data goes out of date: a product is discontinued, a student leaves, a price list is rebuilt from scratch.
Code story: A shop's stock map holds four items. We erase the pencils and watch the size drop from 4 to 3, then clear everything and the size becomes 0. erase() removes one key; clear() empties the whole map.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> stock;
stock["pen"] = 10;
stock["pencil"] = 20;
stock["eraser"] = 15;
stock["ruler"] = 5;
cout << "Before: " << stock.size() << endl;
stock.erase("pencil");
cout << "After erase: " << stock.size() << endl;
stock.clear();
cout << "After clear: " << stock.size() << endl;
return 0;
}Before: 4 After erase: 3 After clear: 0
What just happened? Four items went in, so the size was 4. erase("pencil") removed exactly one pair, leaving 3. clear() wiped the map clean, so the size became 0.
8. A Real Example: Word Frequency Counter
Let us put everything together in a program you will see again and again in real code: counting how often each word appears. A map is the perfect tool β the word is the key, and the count is the value. Every time a word shows up, we add 1 to its count.
Code story: This program counts the words in a sentence. It reads the sentence one character at a time, building words letter by letter. Each finished word becomes a key in the map, and its count goes up by one. At the end we print the number of different words and list each word with its count β already in alphabetical order, thanks to the map.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
string sentence = "the cat sat on the mat the sun";
map<string, int> words;
string word = "";
for (char ch : sentence) {
if (ch == ' ') {
words[word]++;
word = "";
} else {
word += ch;
}
}
if (word != "") words[word]++;
cout << "Unique words: " << words.size() << endl;
for (auto& pair : words) {
cout << "- " << pair.first << ": " << pair.second << endl;
}
return 0;
}Unique words: 6 - cat: 1 - mat: 1 - on: 1 - sat: 1 - sun: 1 - the: 2
What just happened? The sentence has 8 words, but only 6 different ones β "the" appears twice, so its count is 2 and every other word is 1. words[word]++ did the counting: the first time a word appears it starts at 0 and becomes 1, and each repeat adds 1 more. The final loop printed the words in alphabetical order for free.
Which container should you reach for? It depends on whether you need order, pairs, or speed:
| Container | Keys sorted? | Stores pairs? | Best for |
|---|---|---|---|
std::map | Yes, always | Yes, key β value | Lookups by key, in order |
std::unordered_map | No, any order | Yes, key β value | The fastest key lookups |
std::set | Yes, always | No, keys only | Unique values in order |
9. Common Mistakes
Watch out for these five traps β every beginner meets at least one of them:
| Mistake | What happens | Fix |
|---|---|---|
Using m["key"] just to check a key exists | It inserts the key with a default value, growing your map by accident | Use count() or find() to check first |
| Using at() on a key that is not there | The program throws an exception and may crash | Check with count() first, or catch the exception |
| Forgetting that the same key cannot appear twice | The second value silently replaces the first | Use insert() and check its result, or use a multimap |
| Expecting keys to stay in the order you added them | Iterating prints keys in sorted order, not insertion order | If the order of adding matters, keep a vector of pairs alongside |
| Forgetting #include <map> | The compiler says "map was not declared in this scope" | Add #include <map> at the top of the file |
Practice β try it yourself
1. Build a map of five countries and their capitals, then print every country and its capital in alphabetical order.
2. Write a program that counts how many times each letter appears in your name, using a map from char to int.
3. Add five students and their marks to a map, then use find() to check whether "Maya" is present and print her marks if she is.
4. Build two maps β item prices and item quantities β and print the total cost of everything in the shop.
What is a std::map?
What does m["key"] do if "key" is not in the map?
Which function returns an iterator to the first element whose key is not less than the given key?
Fill in the blanks β type the missing word into the box.
Keys in a std::map are always kept in order.
Use m.("apple") to check whether the key "apple" is in the map (the answer is 0 or 1).
Each element of a map is an std:: with .first for the key and .second for the value.
UP NEXT
Lesson 43 β Unordered Maps
Next, meet the unordered_map β the same keyβvalue idea, but with no sorting at all and lookups that are even faster. See when speed beats order.
Go to Lesson 43 β