std::map


STL Container: std::map

std::map is an associative container in the C++ Standard Template Library (STL) that stores key-value pairs in a sorted order based on the keys. It is typically implemented as a balanced binary search tree (usually a red-black tree), providing logarithmic time complexity for insertion, deletion, and search operations.

Key Characteristics

Example 1: Basic Usage

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

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

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

    // Accessing elements
    std::cout << "Alice's age: " << ages["Alice"] << std::endl;

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

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

    // Size of the map
    std::cout << "Number of entries: " << ages.size() << std::endl;

    // Removing an element
    ages.erase("Bob");

    return 0;
}

Explanation:

Example 2: Custom Comparator for Descending Order

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

// Custom comparator for descending order of keys
struct DescendingOrder {
    bool operator()(const std::string& a, const std::string& b) const {
        return a > b;
    }
};

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

    scores["Alice"] = 95;
    scores["Bob"] = 89;
    scores["Charlie"] = 92;
    scores["David"] = 78;

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

    return 0;
}

Explanation:

Example 3: Using map with Custom Objects

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

class Student {
public:
    Student(const std::string& name, int age) : name(name), age(age) {}

    std::string getName() const { return name; }
    int getAge() const { return age; }

private:
    std::string name;
    int age;
};

// Custom comparator for Student objects
struct StudentComparator {
    bool operator()(const Student& a, const Student& b) const {
        return a.getName() < b.getName();
    }
};

int main() {
    std::map<Student, double, StudentComparator> studentGrades;

    studentGrades[Student("Alice", 20)] = 3.8;
    studentGrades[Student("Bob", 22)] = 3.5;
    studentGrades[Student("Charlie", 21)] = 3.9;

    for (const auto& pair : studentGrades) {
        std::cout << pair.first.getName() << " (Age: " << pair.first.getAge() 
                  << "): GPA = " << pair.second << std::endl;
    }

    return 0;
}

Explanation:

Example 4: Nested Maps and Complex Data Structures

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

// Function to print the course roster
void printCourseRoster(const std::map<std::string, std::map<std::string, std::vector<int>>>& university) {
    for (const auto& dept : university) {
        std::cout << "Department: " << dept.first << std::endl;
        for (const auto& course : dept.second) {
            std::cout << "  Course: " << course.first << ", Students: ";
            for (int studentId : course.second) {
                std::cout << studentId << " ";
            }
            std::cout << std::endl;
        }
        std::cout << std::endl;
    }
}

int main() {
    // Map structure: Department -> (Course -> List of Student IDs)
    std::map<std::string, std::map<std::string, std::vector<int>>> university;

    // Adding data
    university["Computer Science"]["Programming 101"] = {1001, 1002, 1003};
    university["Computer Science"]["Data Structures"] = {1002, 1004, 1005};
    university["Mathematics"]["Calculus I"] = {1001, 1005, 1006};
    university["Mathematics"]["Linear Algebra"] = {1003, 1004, 1006};

    // Print the course roster
    printCourseRoster(university);

    // Adding a new student to a course
    university["Computer Science"]["Programming 101"].push_back(1007);

    // Checking if a department exists
    std::string deptToCheck = "Physics";
    if (university.find(deptToCheck) == university.end()) {
        std::cout << deptToCheck << " department does not exist." << std::endl;
    }

    return 0;
}

Explanation:

Additional Considerations

  1. Performance: While map provides good performance for most operations, consider using std::unordered_map for faster lookup times if sorting is not required.

  2. Memory Usage: map typically uses more memory than a simple array due to its tree structure.

  3. Key Immutability: Once a key-value pair is inserted, the key cannot be modified. To change a key, you must remove the old pair and insert a new one.

  4. Value Modification: Values can be modified directly through iterators or using the [] operator.

  5. Default-Constructed Values: Using the [] operator on a non-existent key will insert a default-constructed value.

Summary

std::map is a versatile container in C++ for storing key-value pairs in a sorted order. Key points to remember:

std::map is particularly useful in situations where you need to: - Implement dictionaries or lookup tables - Maintain sorted associations between keys and values - Represent hierarchical data - Perform range-based queries on keys

Understanding when to use std::map versus other containers like std::unordered_map or std::vector is crucial for writing efficient and clear C++ code in various application domains. Its ordered nature makes it ideal for scenarios where both fast lookup and sorted traversal are important.

Related

Previous Page | Course Schedule | Course Content