C++ Maps

Understanding key-value pair containers in C++

๐Ÿ—บ๏ธ What are Maps?

Maps are associative containers that store key-value pairs. Keys are unique and automatically sorted, allowing fast lookup, insertion, and deletion operations based on keys rather than positions.


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

int main() {
    map<string, int> ages;
    ages["Alice"] = 25;
    ages["Bob"] = 30;
    
    cout << "Alice is " << ages["Alice"] << " years old"; // Output: 25
    return 0;
}
                                    

Types of Maps

๐Ÿ”‘

map

Unique keys in sorted order

map<string, int> uniqueMap;
๐Ÿ”„

multimap

Allows duplicate keys, sorted

multimap<string, int> duplicateMap;
โšก

unordered_map

Unique keys, hash-based, faster

unordered_map<string, int> hashMap;
๐Ÿ”€

unordered_multimap

Duplicate keys, hash-based

unordered_multimap<string, int> hashMultiMap;

๐Ÿ”น Basic Map Operations

Maps store key-value pairs in sorted order based on keys, enabling fast lookup, insertion, and deletion. Essential operations include insert() to add pairs, find() to search by key, and erase() to remove elements. Accessing a value via operator[] automatically creates a default entry if the key is missing. Maps are ideal for dictionaries, caches, or any scenario requiring associative access with unique keys.

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

int main() {
    map<string, int> scores;
    
    // Insert key-value pairs
    scores["Alice"] = 95;
    scores["Bob"] = 87;
    scores["Charlie"] = 92;
    scores.insert({"David", 88});
    
    cout << "Map size: " << scores.size() << endl;        // Size: 4
    
    // Access elements
    cout << "Alice's score: " << scores["Alice"] << endl; // 95
    
    // Check if key exists
    if (scores.find("Bob") != scores.end()) {
        cout << "Bob found with score: " << scores["Bob"] << endl;
    }
    
    // Iterate through map (keys are sorted)
    cout << "All scores:" << endl;
    for (auto& pair : scores) {
        cout << pair.first << ": " << pair.second << endl;
    }
    
    return 0;
}

Output:

Map size: 4
Alice's score: 95
Bob found with score: 87
All scores:
Alice: 95
Bob: 87
Charlie: 92
David: 88

๐Ÿ”น Multimap Example

Multimaps allow multiple values per key, unlike standard maps which enforce key uniqueness. This is useful for one-to-many relationships, like storing all phone numbers for a contact name. Elements are still ordered by key. You can insert multiple pairs with the same key and retrieve them using equal_range() to get a range of iterators. Multimaps are implemented as sorted associative containers, providing logarithmic complexity for lookups.

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

int main() {
    multimap<string, string> contacts;
    
    // Insert multiple phone numbers for same person
    contacts.insert({"John", "123-456-7890"});
    contacts.insert({"John", "987-654-3210"});
    contacts.insert({"Jane", "555-123-4567"});
    contacts.insert({"John", "111-222-3333"});
    
    cout << "Total contacts: " << contacts.size() << endl;     // Size: 4
    cout << "John's numbers: " << contacts.count("John") << endl; // Count: 3
    
    // Find all numbers for John
    cout << "John's phone numbers:" << endl;
    auto range = contacts.equal_range("John");
    for (auto it = range.first; it != range.second; ++it) {
        cout << "  " << it->second << endl;
    }
    
    return 0;
}

Output:

Total contacts: 4
John's numbers: 3
John's phone numbers:
ย ย 111-222-3333
ย ย 123-456-7890
ย ย 987-654-3210

๐Ÿ”น Unordered Map Example

Unordered maps use hash tables to provide average constant-time lookups, insertions, and deletions. They store key-value pairs without maintaining any order. Functions like find() and insert() are typically faster than ordered maps for large datasets. However, they require a good hash function for the key type. Unordered maps are perfect when you need fast access and donโ€™t care about sorted iteration, such as in caching or frequency counting.

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

int main() {
    unordered_map<string, double> prices;
    
    // Insert products and prices
    prices["Apple"] = 1.50;
    prices["Banana"] = 0.75;
    prices["Orange"] = 2.00;
    prices["Grape"] = 3.25;
    
    cout << "Store inventory:" << endl;
    for (auto& item : prices) {
        cout << item.first << ": $" << item.second << endl;
    }
    
    // Fast lookup - O(1) average
    string product = "Apple";
    if (prices.find(product) != prices.end()) {
        cout << "\n" << product << " costs $" << prices[product] << endl;
    }
    
    // Update price
    prices["Apple"] = 1.75;
    cout << "Updated Apple price: $" << prices["Apple"] << endl;
    
    return 0;
}

Output:

Store inventory:
Grape: $3.25
Orange: $2
Banana: $0.75
Apple: $1.5

Apple costs $1.5
Updated Apple price: $1.75

๐Ÿ”น Map Operations Summary

All map types (ordered, unordered, multi) support core operations like insertion, lookup, and deletion. Key functions include insert(), find(), erase(), size(), and empty(). Ordered maps (std::map) maintain sorted keys and offer lower_bound() for range queries. Unordered maps (std::unordered_map) provide faster average access via hashing. Choosing the right map depends on whether you need ordering, unique keys, or maximum speed.

Essential Map Operations:

  • map[key] = value: Insert or update key-value pair
  • insert({key, value}): Insert new pair
  • find(key): Search for key, returns iterator
  • count(key): Count occurrences of key
  • erase(key): Remove key-value pair
  • size(): Get number of pairs
  • empty(): Check if map is empty
  • clear(): Remove all pairs

Performance Comparison:

  • map: O(log n) operations, sorted keys
  • unordered_map: O(1) average, O(n) worst case

๐Ÿง  Test Your Knowledge

What happens when you access a non-existent key with map[key]?