2025-11-27 C++ map与set容器详解:使用指南与实践应用

# C++ map与set容器详解:使用指南与实践应用

map和set是C++标准模板库中最重要的关联容器,它们基于红黑树实现,提供了高效的元素存储和检索能力。掌握这两种容器的使用对于编写高质量的C++程序至关重要。

## 关联容器概述

关联容器与序列容器的主要区别在于元素的组织方式。序列容器按位置存储元素,而关联容器按关键字存储元素,支持高效的关键字查找和访问。

### 主要关联容器类型

- **set**:只包含关键字的容器,元素不可重复

- **map**:包含键值对的容器,键不可重复

- **multiset**:允许重复关键字的set

- **multimap**:允许重复键的map

## set容器深度解析

### 基本定义与初始化

```cpp

#include <set>

#include <iostream>

#include <string>

void set_basic_usage() {

    // 默认初始化

    std::set<int> numbers;


    // 初始化列表初始化

    std::set<std::string> fruits = {"apple", "banana", "orange"};


    // 拷贝构造

    std::set<std::string> fruits_copy(fruits);


    // 范围初始化

    std::vector<int> vec = {5, 2, 8, 1, 9};

    std::set<int> sorted_numbers(vec.begin(), vec.end());


    // 指定比较函数

    std::set<int, std::greater<int>> descending_numbers = {1, 2, 3, 4, 5};

}

```

### 元素操作

```cpp

void set_operations() {

    std::set<int> numbers;


    // 插入元素

    numbers.insert(10);

    numbers.insert(20);

    numbers.insert(30);

    numbers.insert(20); // 重复元素,不会插入


    // 插入结果处理

    auto result = numbers.insert(40);

    if (result.second) {

        std::cout << "Insertion successful" << std::endl;

    }


    // 删除元素

    numbers.erase(10);          // 通过值删除

    auto it = numbers.find(20);

    if (it != numbers.end())<"CBA.2597.HK"> {

        numbers.erase(it);      // 通过迭代器删除

    }


    // 范围删除

    numbers.erase(numbers.begin(), std::next(numbers.begin(), 2));

}

```

### 查找与统计

```cpp

void set_lookup() {

    std::set<std::string> words = {"hello", "world", "cpp", "programming"};


    // find方法

    auto it = words.find("cpp");

    if (it != words.end()) {

        std::cout << "Found: " << *it << std::endl;

    }


    // count方法

    if (words.count("hello") > 0) {

        std::cout << "hello exists in set" << std::endl;

    }


    // 范围查找

    std::set<int> numbers = {10, 20, 30, 40, 50, 60};

    auto lower = numbers.lower_bound(25); // 第一个 >= 25 的元素

    auto upper = numbers.upper_bound(45); // 第一个 > 45 的元素


    std::cout << "Range [25, 45]: ";

    for (auto iter = lower; iter != upper; ++iter) {

        std::cout << *iter << " ";

    }

    std::cout << std::endl;


    // equal_range方法

    auto range = numbers.equal_range(30);

    for (auto iter = range.first; iter != range.second; ++iter) {

        std::cout << *iter << " ";

    }

    std::cout << std::endl;

}

```

## map容器深度解析

### 基本定义与初始化

```cpp

#include <map>

#include <string>

void map_basic_usage() {

    // 默认初始化

    std::map<std::string, int> age_map;


    // 初始化列表初始化

    std::map<std::string, double> price_map = {

        {"apple", 1.20},

        {"banana", 0.80},

        {"orange", 1.50}

    };


    // 指定比较函数

    std::map<std::string, int, std::greater<std::string>> reverse_map;

}

```

### 元素访问与修改

```cpp

void map_access_operations() {

    std::map<std::string, int> scores;


    // 插入元素

    scores.insert({"Alice", 90});

    scores.insert(std::make_pair("Bob", 85));

    scores["Charlie"] = 95;  // 使用下标操作符


    // 访问元素

    std::cout << "Alice's score: " << scores["Alice"] << std::endl;


    // 安全访问(避免自动插入)

    auto it = scores.find("David");

    if (it != scores.end()) {

        std::cout << "David's score: " << it->second << std::endl;

    } else {

        std::cout << "David not found" << std::endl;

    }


    // 使用at方法(边界检查)

    try {

        int score = scores.at("Eve");

    } catch (const std::out_of_range& e) {

        std::cout <"ABC.2597.HK"><< "Key not found: " << e.what() << std::endl;

    }


    // 修改元素

    scores["Alice"] = 92;          // 直接修改

    it = scores.find("Bob");

    if (it != scores.end()) {

        it->second = 88;            // 通过迭代器修改

    }

}

```

### 遍历操作

```cpp

void map_traversal() {

    std::map<std::string, int> population = {

        {"Beijing", 21540000},

        {"Shanghai", 24280000},

        {"Guangzhou", 14040000},

        {"Shenzhen", 12530000}

    };


    // 使用迭代器遍历

    std::cout << "City populations:" << std::endl;

    for (auto it = population.begin(); it != population.end(); ++it) {

        std::cout << it->first << ": " << it->second << std::endl;

    }


    // 使用范围for循环遍历

    std::cout << "\nUsing range-based for loop:" << std::endl;

    for (const auto& pair : population) {

        std::cout << pair.first << ": " << pair.second << std::endl;

    }


    // 使用结构化绑定(C++17)

    std::cout << "\nUsing structured bindings:" << std::endl;

    for (const auto& [city, pop] : population) {

        std::cout << city << ": " << pop << std::endl;

    }

}

```

## 实际应用案例

### 单词频率统计

```cpp

#include <sstream>

#include <map>

#include <string>

#include <cctype>

std::map<std::string, int> word_frequency(const std::string& text) {

    std::map<std::string, int> freq;

    std::istringstream iss(text);

    std::string word;


    while (iss >> word) {

        // 清理单词(移除标点,转为小写)

        for (char& c : word) {

            c = std::tolower(c);

        }


        // 移除末尾标点

        if (!word.empty() && std::ispunct(word.back())) {

            word.pop_back();

        }


        if (!word.empty()) {

            ++freq[word];

        }

    }


    return freq;

}

void print_word_frequency(const std::map<std::string, int>& freq) {

    for (const auto& [word, count] : freq) {

        std::cout << word << ": " << count << std::endl;

    }

}

```

### 学生成绩管理系统

```cpp

class StudentManager {

private:

    std::map<int, std::string> student_names;

    std::map<int, double> student_scores;


public:

    void add_student(int id, const std::string& name, double score) {

        student_names[id] = name;

        student_scores[id] = score;

    }


    void remove_student(int id) {

        student_names.erase(id);

        student_scores.erase(id);

    }


    void update_score(int id, double new_score) {

        auto it = student_scores.find(id);

        if (it != student_scores.end()) {

            it->second = new_score;

        }

    }


    void print_students_by_id() const {

        for (const auto& [id, name] : student_names) {

            std::cout <"BCA.2597.HK"><< "ID: " << id << ", Name: " << name

                      << ", Score: " << student_scores.at(id) << std::endl;

        }

    }


    void find_student(int id) const {

        auto name_it = student_names.find(id);

        auto score_it = student_scores.find(id);


        if (name_it != student_names.end() && score_it != student_scores.end()) {

            std::cout << "Found student - ID: " << id

                      << ", Name: " << name_it->second

                      << ", Score: " << score_it->second << std::endl;

        } else {

            std::cout << "Student not found" << std::endl;

        }

    }

};

```

## 性能特性分析

### 时间复杂度比较

```cpp

void performance_characteristics() {

    std::set<int> test_set;

    std::map<int, std::string> test_map;


    // 插入操作:O(log n)

    for (int i = 0; i < 1000; ++i) {

        test_set.insert(i);

        test_map[i] = "value" + std::to_string(i);

    }


    // 查找操作:O(log n)

    auto set_found = test_set.find(500);

    auto map_found = test_map.find(500);


    // 删除操作:O(log n)

    test_set.erase(500);

    test_map.erase(500);


    // 遍历操作:O(n)

    for (const auto& num : test_set) {

        // 遍历所有元素

    }


    for (const auto& pair : test_map) {

        // 遍历所有键值对

    }

}

```

## 自定义比较函数

### 自定义类型作为键

```cpp

struct Student {

    int id;

    std::string name;


    bool operator<(const Student& other) const {

        return id < other.id;

    }

};

void custom_key_type() {

    std::set<Student> students;

    students.insert({1001, "Alice"});

    students.insert({1002, "Bob"});

    students.insert({1003, "Charlie"});


    std::map<Student, double> student_scores;

    student_scores[{1001, "Alice"}] = 85.5;

    student_scores[{1002, "Bob"}] = 92.0;

}

// 使用函数对象作为比较器

struct StudentComparator {

    bool operator()(const Student& a, const Student& b) const {

        return a.name < b.name;

    }

};

void custom_comparator() {

    std::set<Student, StudentComparator> students_by_name;

    students_by_name.insert({1001, "Charlie"});

    students_by_name.insert({1002, "Alice"});

    students_by_name.insert({1003, "Bob"});


    // 学生将按姓名排序:Alice, Bob, Charlie

}

```

## 高级用法与技巧

### 使用emplace提高效率

```cpp

void emplace_usage() {

    std::set<std::string> words;

    std::map<int, std::string> data;


    // 使用emplace避免临时对象

    words.emplace("hello");

    words.emplace<"CBA.2597.HK">("world");


    data.emplace(1, "value1");

    data.emplace(std::piecewise_construct,

                std::forward_as_tuple(2),

                std::forward_as_tuple("value2"));


    // 处理emplace结果

    auto result = data.emplace(3, "value3");

    if (result.second) {

        std::cout << "Insertion successful" << std::endl;

    }

}

```

### 提取节点功能(C++17)

```cpp

void node_operations() {

    std::map<int, std::string> source = {{1, "one"}, {2, "two"}, {3, "three"}};

    std::map<int, std::string> target;


    // 提取节点(不复制不移动)

    auto node = source.extract(2);

    if (!node.empty()) {

        target.insert(std::move(node));

    }


    // 合并两个map

    std::map<int, std::string> other = {{4, "four"}, {5, "five"}};

    target.merge(other);


    std::cout << "Source size: " << source.size() << std::endl;

    std::cout << "Target size: " << target.size() << std::endl;

    std::cout << "Other size: " << other.size() << std::endl;

}

```

## 最佳实践与注意事项

### 内存管理

```cpp

void memory_management() {

    // 预分配空间(对于已知大小的容器)

    std::set<int> large_set;

    // 注意:set/map没有reserve方法,因为基于树结构


    // 及时清理不需要的元素

    std::map<int, std::string> temp_data;

    // ... 使用temp_data

    temp_data.clear();  // 显式清理


    // 使用局部作用域控制生命周期

    {

        std::set<int> temporary_set;

        // 使用temporary_set

    } // temporary_set自动销毁

}

```

### 迭代器安全

```cpp

void iterator_safety() {

    std::map<int, std::string> data = {{1, "a"}, {2, "b"}, {3, "c"}};


    // 安全的遍历中删除

    for (auto it = data.begin(); it != data.end(); ) {

        if (it->first % 2 == 0) {

            it = data.erase(it);  // erase返回下一个有效的迭代器

        } else {

            ++it;

        }

    }


    // 避免在迭代过程中修改键

    for (auto& pair : data) {

        // pair.first = 10;  // 错误!不能修改键

        pair.second = "modified";  // 可以修改值

    }

}

```

## 总结

map和set作为C++标准库中基于红黑树实现的关联容器,提供了高效的元素存储和检索能力。它们保证了元素的有序性,支持对数时间复杂度的查找、插入和删除操作。

在实际开发中,应根据具体需求选择合适的容器:当需要存储键值对且键唯一时使用map,当只需要存储唯一元素时使用set。对于允许重复元素的情况,可以考虑multimap和multiset。

掌握这些容器的特性和最佳实践,能够帮助开发者编写出更高效、更安全的C++代码。理解其底层实现原理也有助于在复杂场景下做出正确的设计决策。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容