map


Header: <map>

The <map> header is part of the C++ Standard Template Library (STL) that provides the std::map and std::multimap container classes. These containers implement sorted associative containers, which store key-value pairs and keep them sorted by keys. std::map ensures unique keys, while std::multimap allows multiple entries with the same key.

Key Characteristics

Example: Basic Usage of std::map

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> age_map;

    // Inserting elements
    age_map["Alice"] = 30;
    age_map.insert({"Bob", 25});
    age_map.insert(std::make_pair("Charlie", 35));

    // Accessing elements
    std::cout << "Alice's age: " << age_map["Alice"] << std::endl;
    std::cout << "Bob's age: " << age_map.at("Bob") << std::endl;

    // Checking if a key exists
    if (age_map.count("David") == 0) {
        std::cout << "David is not in the map" << std::endl;
    }

    // Iterating through the map
    for (const auto& pair : age_map) {
        std::cout << pair.first << " is " << pair.second << " years old" << std::endl;
    }

    // Size of the map
    std::cout << "Map size: " << age_map.size() << std::endl;

    return 0;
}

Explanation:

Example 2: Using Custom Comparator with std::map

#include <iostream>
#include <map>
#include <string>

// Custom comparator for case-insensitive string comparison
struct CaseInsensitiveCompare {
    bool operator()(const std::string& a, const std::string& b) const {
        return std::lexicographical_compare(
            a.begin(), a.end(), b.begin(), b.end(),
            [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }
        );
    }
};

int main() {
    std::map<std::string, int, CaseInsensitiveCompare> scores;

    scores["Alice"] = 100;
    scores["bob"] = 85;
    scores["CHARLIE"] = 90;

    // This will update "Alice" instead of creating a new entry
    scores["aLiCe"] = 95;

    for (const auto& pair : scores) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

Explanation:

Example 3: Using std::multimap

#include <iostream>
#include <map>
#include <string>

int main() {
    std::multimap<std::string, int> grades;

    // Inserting multiple values for the same key
    grades.insert({"Alice", 85});
    grades.insert({"Bob", 90});
    grades.insert({"Alice", 92});
    grades.insert({"Charlie", 88});
    grades.insert({"Bob", 95});

    // Printing all entries
    for (const auto& grade : grades) {
        std::cout << grade.first << ": " << grade.second << std::endl;
    }

    // Finding all grades for a specific student
    std::string student = "Bob";
    auto range = grades.equal_range(student);
    std::cout << "\nGrades for " << student << ":" << std::endl;
    for (auto it = range.first; it != range.second; ++it) {
        std::cout << it->second << std::endl;
    }

    // Counting entries for a key
    std::cout << "\nNumber of grades for Alice: " << grades.count("Alice") << std::endl;

    return 0;
}

Explanation:

Example 4: Map Operations and Modifications

#include <iostream>
#include <map>
#include <string>

void printMap(const std::map<std::string, int>& m) {
    for (const auto& pair : m) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }
    std::cout << std::endl;
}

int main() {
    std::map<std::string, int> inventory;

    // Inserting elements
    inventory["apple"] = 5;
    inventory["banana"] = 8;
    inventory["orange"] = 10;

    std::cout << "Initial inventory:" << std::endl;
    printMap(inventory);

    // Modifying an element
    inventory["apple"] = 7;

    // Erasing an element
    inventory.erase("orange");

    std::cout << "After modifications:" << std::endl;
    printMap(inventory);

    // Finding an element
    auto it = inventory.find("banana");
    if (it != inventory.end()) {
        std::cout << "Found " << it->first << " with quantity " << it->second << std::endl;
    }

    // Inserting with hint
    auto hint = inventory.lower_bound("cherry");
    inventory.insert(hint, {"cherry", 15});

    std::cout << "After inserting cherry:" << std::endl;
    printMap(inventory);

    // Using emplace
    inventory.emplace("grape", 20);

    std::cout << "Final inventory:" << std::endl;
    printMap(inventory);

    return 0;
}

Explanation:

Example 5: Map with Pair as Key

#include <iostream>
#include <map>
#include <string>

// Custom comparator for pair<int, string>
struct PairCompare {
    bool operator()(const std::pair<int, std::string>& lhs, 
                    const std::pair<int, std::string>& rhs) const {
        if (lhs.first != rhs.first)
            return lhs.first < rhs.first;
        return lhs.second < rhs.second;
    }
};

int main() {
    std::map<std::pair<int, std::string>, double, PairCompare> scores;

    scores[{1, "Alice"}] = 95.5;
    scores[{2, "Bob"}] = 89.0;
    scores[{1, "Charlie"}] = 91.5;
    scores[{3, "David"}] = 87.5;

    for (const auto& entry : scores) {
        std::cout << "Student ID: " << entry.first.first 
                  << ", Name: " << entry.first.second 
                  << ", Score: " << entry.second << std::endl;
    }

    // Finding a specific entry
    auto it = scores.find({1, "Alice"});
    if (it != scores.end()) {
        std::cout << "\nFound: " << it->first.second 
                  << " (ID: " << it->first.first 
                  << ") with score " << it->second << std::endl;
    }

    return 0;
}

Explanation:

Additional Considerations

  1. Performance: Map operations have logarithmic complexity, which is efficient for most use cases but may be slower than unordered_map for very large datasets.

  2. Memory Usage: Maps typically use more memory than unordered containers due to the overhead of maintaining the sorted structure.

  3. Iterator Invalidation: Iterators and references to map elements remain valid after insertion or removal of other elements.

  4. Comparison Requirements: The key type must support the < operator or a custom comparator must be provided.

  5. No Direct Data Access: Unlike vectors, map elements cannot be accessed directly by index.

Summary

The <map> header in C++ provides the std::map and std::multimap containers, which are powerful tools for storing and manipulating key-value pairs:

Maps are particularly useful when you need to maintain a sorted collection of key-value pairs with efficient lookup, insertion, and deletion operations. They are commonly used in scenarios such as dictionaries, indexes, and any situation where data needs to be associated with unique identifiers and kept in a specific order.

While maps provide powerful functionality, it's important to consider the specific requirements of your application. For cases where sorting is not necessary and faster average-case performance is desired, std::unordered_map might be a better choice. Understanding the characteristics and proper usage of maps can significantly enhance the efficiency and clarity of C++ programs dealing with associative data.

Related

Previous Page | Course Schedule | Course Content