跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++关联式容器详解:map、set与unordered_map的原理与应用

综述由AI生成C++关联式容器涵盖map、set与unordered_map,分别基于红黑树与哈希表实现。解析了它们的特性、通用接口及具体用法,包括键值对存储、唯一元素管理及快速查找机制。通过性能测试代码对比了不同容器在大数据量下的耗时差异,并结合电话簿案例展示了实际应用场景。最后提供了选择策略与练习建议,帮助开发者根据有序性、查找效率等需求优化数据结构选型。

蜜桃汽水发布于 2026/2/20更新于 2026/6/225 浏览
C++关联式容器详解:map、set与unordered_map的原理与应用

C++关联式容器详解:map、set与unordered_map的原理与应用

一、学习目标与重点

本章将深入探讨C++标准库中最常用的关联式容器——map、set和unordered_map的核心原理与应用。通过学习,你将能够:

  1. 掌握关联式容器的基本特性与适用场景,理解它们在数据组织和查找方面的优势
  2. 熟练使用map容器,掌握键值对存储与访问的方法
  3. 理解set容器的集合特性,学会处理唯一元素的存储
  4. 了解unordered_map的哈希表实现,掌握快速查找的原理
  5. 学会选择合适的关联式容器,根据应用场景优化程序性能
  6. 培养键值对思维,将问题抽象为键值对的映射关系

二、关联式容器概述

2.1 关联式容器的基本概念

关联式容器是一种将元素与键值关联起来的数据结构,分为两类:

  • 有序关联式容器:元素按键值有序存储,如map、set、multimap、multiset
  • 无序关联式容器:元素按哈希值存储,如unordered_map、unordered_set、unordered_multimap、unordered_multiset
2.2 关联式容器的通用接口

所有关联式容器都提供了通用的接口:

  • size():获取容器大小
  • empty():判断容器是否为空
  • begin()/end():获取迭代器
  • insert():插入元素
  • erase():删除元素
  • clear():清空容器
  • find():查找元素

三、map容器

3.1 map的基本特性

map是一个有序键值对容器,具有以下特性:

  • 元素按键值有序存储(默认升序)
  • 键值唯一,每个键对应一个值
  • 支持快速查找(O(log n)时间复杂度)
  • 插入和删除操作效率高(O(log n)时间复杂度)
3.2 map的使用方法
#include <iostream>
#include <map>
#include <string>

int main() {
    std::cout << "=== map容器示例 ===" << std::endl;
    // 创建 map
    std::map<int, std::string> idToName;
    std::map<std::string, > nameToAge;

    
    idToName[] = ;
    idToName[] = ;
    idToName[] = ;
    nameToAge[] = ;
    nameToAge[] = ;
    nameToAge[] = ;

    
    std::cout <<  << idToName[] << std::endl;
    std::cout <<  << nameToAge[] << std::endl;

    
    std::cout <<  << std::endl;
     ( & pair : idToName) {
        std::cout <<  << pair.first <<  << pair.second << std::endl;
    }
    std::cout <<  << std::endl;
     (std::map<std::string, >::const_iterator it = nameToAge.(); it != nameToAge.(); ++it) {
        std::cout <<  << it->first <<  << it->second << std::endl;
    }

    
     findResult = idToName.();
     (findResult != idToName.()) {
        std::cout <<  << findResult->second << std::endl;
    }

    
    idToName.(std::(, )); 
    std::cout <<  << std::endl;
     ( & pair : idToName) {
        std::cout <<  << pair.first <<  << pair.second << std::endl;
    }
    idToName.(); 
    std::cout <<  << std::endl;
     ( & pair : idToName) {
        std::cout <<  << pair.first <<  << pair.second << std::endl;
    }

    
    std::cout <<  << idToName.() << std::endl;
    std::cout <<  << idToName.() << std::endl;
    std::cout <<  << (idToName.() > ) << std::endl;
     ;
}
int
// 添加元素
1
"张三"
2
"李四"
3
"王五"
"张三"
25
"李四"
30
"王五"
35
// 访问元素
"idToName[1]: "
1
"nameToAge[\"李四\"]: "
"李四"
// 遍历元素
"遍历 idToName: "
for
const
auto
"ID: "
", 姓名:"
"遍历 nameToAge: "
for
int
begin
end
"姓名:"
", 年龄:"
// 查找元素
auto
find
2
if
end
"找到 ID 为 2 的姓名:"
// 插入和删除
insert
make_pair
4
"赵六"
// 插入元素
"插入后的 idToName: "
for
const
auto
"ID: "
", 姓名:"
erase
3
// 删除元素
"删除后的 idToName: "
for
const
auto
"ID: "
", 姓名:"
// 其他操作
"idToName 大小:"
size
"idToName 是否为空:"
empty
"idToName 包含 ID 为 1: "
count
1
0
return
0
3.3 map的应用场景

map常用于需要键值对映射的场景,如:

  • 数据库查询结果存储
  • 配置文件解析
  • 缓存系统
  • 统计分析

四、set容器

4.1 set的基本特性

set是一个有序集合容器,具有以下特性:

  • 元素按值有序存储(默认升序)
  • 元素值唯一
  • 支持快速查找(O(log n)时间复杂度)
  • 插入和删除操作效率高(O(log n)时间复杂度)
4.2 set的使用方法
#include <iostream>
#include <set>
#include <string>

int main() {
    std::cout << "=== set容器示例 ===" << std::endl;
    // 创建 set
    std::set<int> numbers;
    std::set<std::string> names;

    // 添加元素
    numbers.insert(10);
    numbers.insert(20);
    numbers.insert(30);
    numbers.insert(20); // 重复元素不会插入
    names.insert("张三");
    names.insert("李四");
    names.insert("王五");

    // 遍历元素
    std::cout << "遍历 numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    std::cout << "遍历 names: ";
    for (const std::string& name : names) {
        std::cout << name << " ";
    }
    std::cout << std::endl;

    // 查找元素
    auto findResult = numbers.find(20);
    if (findResult != numbers.end()) {
        std::cout << "找到元素 20" << std::endl;
    }

    // 插入和删除
    numbers.insert(40); // 插入元素
    std::cout << "插入后的 numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    numbers.erase(30); // 删除元素
    std::cout << "删除后的 numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 其他操作
    std::cout << "numbers 大小:" << numbers.size() << std::endl;
    std::cout << "numbers 是否为空:" << numbers.empty() << std::endl;
    std::cout << "numbers 包含 20: " << (numbers.count(20) > 0) << std::endl;
    return 0;
}
4.3 set的应用场景

set常用于需要存储唯一元素的场景,如:

  • 去重操作
  • 集合运算(交集、并集、差集)
  • 统计唯一元素个数
  • 成员资格判断

五、unordered_map容器

5.1 unordered_map的基本特性

unordered_map是一个无序键值对容器,具有以下特性:

  • 元素按哈希值存储
  • 键值唯一,每个键对应一个值
  • 支持快速查找(O(1)平均时间复杂度)
  • 插入和删除操作效率高(O(1)平均时间复杂度)
5.2 unordered_map的使用方法
#include <iostream>
#include <unordered_map>
#include <string>

int main() {
    std::cout << "=== unordered_map容器示例 ===" << std::endl;
    // 创建 unordered_map
    std::unordered_map<int, std::string> idToName;
    std::unordered_map<std::string, int> nameToAge;

    // 添加元素
    idToName[1] = "张三";
    idToName[2] = "李四";
    idToName[3] = "王五";
    nameToAge["张三"] = 25;
    nameToAge["李四"] = 30;
    nameToAge["王五"] = 35;

    // 访问元素
    std::cout << "idToName[1]: " << idToName[1] << std::endl;
    std::cout << "nameToAge[\"李四\"]: " << nameToAge["李四"] << std::endl;

    // 遍历元素(顺序不确定)
    std::cout << "遍历 idToName: " << std::endl;
    for (const auto& pair : idToName) {
        std::cout << "ID: " << pair.first << ", 姓名:" << pair.second << std::endl;
    }

    // 查找元素
    auto findResult = idToName.find(2);
    if (findResult != idToName.end()) {
        std::cout << "找到 ID 为 2 的姓名:" << findResult->second << std::endl;
    }

    // 插入和删除
    idToName.insert(std::make_pair(4, "赵六")); // 插入元素
    std::cout << "插入后的 idToName: " << std::endl;
    for (const auto& pair : idToName) {
        std::cout << "ID: " << pair.first << ", 姓名:" << pair.second << std::endl;
    }
    idToName.erase(3); // 删除元素
    std::cout << "删除后的 idToName: " << std::endl;
    for (const auto& pair : idToName) {
        std::cout << "ID: " << pair.first << ", 姓名:" << pair.second << std::endl;
    }

    // 其他操作
    std::cout << "idToName 大小:" << idToName.size() << std::endl;
    std::cout << "idToName 是否为空:" << idToName.empty() << std::endl;
    std::cout << "idToName 包含 ID 为 1: " << (idToName.count(1) > 0) << std::endl;
    return 0;
}
5.3 unordered_map的性能分析
#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>
#include <string>

void testMap(int n) {
    auto start = std::chrono::high_resolution_clock::now();
    std::map<int, std::string> map;
    for (int i = 0; i < n; i++) {
        map[i] = "测试字符串" + std::to_string(i);
    }
    // 查找元素
    for (int i = 0; i < n; i++) {
        auto it = map.find(i);
        if (it == map.end()) {
            std::cout << "未找到元素" << i << std::endl;
        }
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "map 操作" << n << "个元素耗时:" << duration.count() << "ms" << std::endl;
}

void testUnorderedMap(int n) {
    auto start = std::chrono::high_resolution_clock::now();
    std::unordered_map<int, std::string> unorderedMap;
    for (int i = 0; i < n; i++) {
        unorderedMap[i] = "测试字符串" + std::to_string(i);
    }
    // 查找元素
    for (int i = 0; i < n; i++) {
        auto it = unorderedMap.find(i);
        if (it == unorderedMap.end()) {
            std::cout << "未找到元素" << i << std::endl;
        }
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "unordered_map 操作" << n << "个元素耗时:" << duration.count() << "ms" << std::endl;
}

int main() {
    std::cout << "=== 关联式容器性能测试 ===" << std::endl;
    int n = 100000;
    testMap(n);
    testUnorderedMap(n);
    return 0;
}

六、关联式容器选择策略

6.1 容器特性对比
特性mapsetunordered_mapunordered_set
存储方式键值对元素值键值对元素值
存储顺序有序(默认升序)有序(默认升序)无序(哈希值)无序(哈希值)
查找时间O(log n)O(log n)O(1)平均O(1)平均
插入时间O(log n)O(log n)O(1)平均O(1)平均
删除时间O(log n)O(log n)O(1)平均O(1)平均
内存开销高(平衡二叉树)高(平衡二叉树)中等(哈希表)中等(哈希表)
适用场景需要有序键值对映射需要有序唯一元素需要快速查找的键值对映射需要快速查找的唯一元素
6.2 容器选择建议
  1. 如果需要有序存储,选择 map 或 set
  2. 如果需要快速查找,选择 unordered_map 或 unordered_set
  3. 如果需要键值对映射,选择 map 或 unordered_map
  4. 如果需要存储唯一元素,选择 set 或 unordered_set
  5. 如果需要频繁插入删除且不需要有序性,选择 unordered_map 或 unordered_set

七、综合案例:实现一个简单的电话簿

让我们通过一个综合案例来应用本章所学的知识:

#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
#include <algorithm>

// 电话簿类
class PhoneBook {
private:
    std::map<std::string, std::string> nameToNumber; // 姓名到电话号码的映射
    std::unordered_map<std::string, std::string> numberToName; // 电话号码到姓名的映射

public:
    // 添加联系人
    void addContact(const std::string& name, const std::string& number) {
        nameToNumber[name] = number;
        numberToName[number] = name;
    }

    // 删除联系人
    void removeContact(const std::string& name) {
        auto it = nameToNumber.find(name);
        if (it != nameToNumber.end()) {
            numberToName.erase(it->second);
            nameToNumber.erase(it);
        }
    }

    // 查找联系人(按姓名)
    std::string findByName(const std::string& name) const {
        auto it = nameToNumber.find(name);
        if (it != nameToNumber.end()) {
            return it->second;
        }
        return "未找到";
    }

    // 查找联系人(按电话号码)
    std::string findByNumber(const std::string& number) const {
        auto it = numberToName.find(number);
        if (it != numberToName.end()) {
            return it->second;
        }
        return "未找到";
    }

    // 打印所有联系人
    void printAllContacts() const {
        std::cout << "=== 电话簿 ===" << std::endl;
        for (const auto& pair : nameToNumber) {
            std::cout << "姓名:" << pair.first << ", 电话:" << pair.second << std::endl;
        }
    }

    // 检查联系人是否存在
    bool contactExists(const std::string& name) const {
        return nameToNumber.count(name) > 0;
    }
};

int main() {
    std::cout << "=== 简单的电话簿 ===" << std::endl;
    PhoneBook phoneBook;

    // 添加联系人
    phoneBook.addContact("张三", "13800138000");
    phoneBook.addContact("李四", "13900139000");
    phoneBook.addContact("王五", "13700137000");

    // 打印所有联系人
    phoneBook.printAllContacts();

    // 查找联系人
    std::string zhangNumber = phoneBook.findByName("张三");
    std::cout << "张三的电话:" << zhangNumber << std::endl;
    std::string liName = phoneBook.findByNumber("13900139000");
    std::cout << "13900139000 的姓名:" << liName << std::endl;

    // 检查联系人是否存在
    bool exists = phoneBook.contactExists("赵六");
    std::cout << "赵六是否存在:" << exists << std::endl;

    // 删除联系人
    phoneBook.removeContact("李四");
    std::cout << "删除李四后的电话簿:" << std::endl;
    phoneBook.printAllContacts();
    return 0;
}

八、总结与练习

8.1 本章总结

本章介绍了C++标准库中三种常用关联式容器的核心知识,包括:

  1. map容器的基本特性、使用方法和应用场景
  2. set容器的集合特性、使用方法和应用场景
  3. unordered_map的哈希表实现、使用方法和性能分析
  4. 关联式容器选择策略,根据应用场景选择合适的容器类型
  5. 综合案例:实现一个简单的电话簿
8.2 练习题
  1. 写一个程序,使用map实现一个简单的单词计数程序。
  2. 编写一个程序,使用set实现一个简单的去重操作。
  3. 写一个程序,使用unordered_map实现一个简单的缓存系统。
  4. 实现一个函数,比较map和unordered_map在不同操作下的性能差异。
  5. 写一个程序,使用综合关联式容器特性实现一个简单的字典系统。
8.3 进阶挑战
  1. 研究C++标准库中的其他关联式容器类型,如multimap、multiset等。
  2. 学习如何使用C++标准库中的算法(如std::sort、std::find等)操作关联式容器。
  3. 研究C++中的哈希函数和哈希表实现,优化unordered_map的性能。
  4. 学习如何使用C++的并发关联式容器(如std::map、std::unordered_map的线程安全版本)。

目录

  1. C++关联式容器详解:map、set与unordered_map的原理与应用
  2. 一、学习目标与重点
  3. 二、关联式容器概述
  4. 2.1 关联式容器的基本概念
  5. 2.2 关联式容器的通用接口
  6. 三、map容器
  7. 3.1 map的基本特性
  8. 3.2 map的使用方法
  9. 3.3 map的应用场景
  10. 四、set容器
  11. 4.1 set的基本特性
  12. 4.2 set的使用方法
  13. 4.3 set的应用场景
  14. 五、unordered_map容器
  15. 5.1 unordered_map的基本特性
  16. 5.2 unordered_map的使用方法
  17. 5.3 unordered_map的性能分析
  18. 六、关联式容器选择策略
  19. 6.1 容器特性对比
  20. 6.2 容器选择建议
  21. 七、综合案例:实现一个简单的电话簿
  22. 八、总结与练习
  23. 8.1 本章总结
  24. 8.2 练习题
  25. 8.3 进阶挑战
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Nginx 配置 HTTPS 实战:前后端集成方案
  • 数据库连接条件下推优化技术解析
  • 在昇腾 NPU 上部署与测评 CodeLlama-7b-Python
  • OpenClaw安装和接入飞书机器人完整教程
  • Higress 网关:REST API 转 MCP Server 配置指南
  • 35 岁程序员失业危机:现状分析与破局策略
  • 多模态融合:RetinaFace、CurricularFace 与语音识别系统
  • Nginx 部署前端 Vue 项目实战
  • 微信小程序样式与布局详解
  • 基于大模型 API 打造个人 AI 助理实战
  • 突破 LLM 上下文瓶颈:上下文内存虚拟化 CMV 的设计与实践
  • MySQL 详细安装配置完整教程
  • OpenClaw 智能体生态崛起及百度腾讯布局解析
  • Linux 部署 OpenClaw 并接入 QQ 机器人指南
  • AI 临床副驾驶实战:基于 Go 的电子病历助手与 HIS 对接
  • VS Code 前端开发:10 款必备插件安装与配置实战
  • AI 生成前端 UI 的三步优化技巧
  • Stable Diffusion 风格库使用指南:833 种艺术风格参考
  • Ubuntu 软件源(镜像源)修改指南
  • Qwen3.5-4B 微调实战:基于 LLaMA-Factory 构建医疗 AI 助手

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online