当前位置: 首页 > news >正文

C++ 第四阶段 STL 容器 - 第九讲:详解 std::map 与 std::unordered_map —— 关联容器的深度解析

目录

📌 一、std::map 与 std::unordered_map 概述

📌 二、std::map 详解

1. 核心特性

2. 常用函数解析

3. 性能优势

📌 三、std::unordered_map 详解

1. 核心特性

2. 常用函数解析

3. 性能优势

📌 四、std::map 与 std::unordered_map 对比

📌 五、性能优化建议

1. std::map 优化

2. std::unordered_map 优化

📌 六、常见陷阱与解决方案

1. 哈希冲突陷阱

2. 键类型不可变陷阱

3. 自定义键类型陷阱

📌 七、典型应用场景

1. std::map 的应用场景

2. std::unordered_map 的应用场景

🧠 总结

C++从入门到入土学习导航_c++学习进程-CSDN博客


📌 一、std::map 与 std::unordered_map 概述

std::mapstd::unordered_map 是 C++ STL 中的 关联容器,用于存储键值对(Key-Value)。它们的核心区别在于:

特性std::mapstd::unordered_map
底层实现红黑树(平衡二叉搜索树)哈希表(基于链地址法或开放寻址法)
有序性键值对按键有序排列键值对无序存储
插入/删除效率O(log n)平均 O(1),最坏 O(n)(哈希冲突严重时)
查找效率O(log n)平均 O(1),最坏 O(n)
内存占用较高(红黑树节点存储父/子指针)较低(哈希表存储桶和键值对)
适用场景需要有序性或范围查询高效查找且无需有序性

📌 二、std::map 详解

1. 核心特性

  • 红黑树实现:基于自平衡二叉搜索树,保证键值对按键有序。
  • 有序性:元素按键升序排列(默认),支持范围查询。
  • 时间复杂度:插入、删除、查找均为 O(log n)。
  • 键唯一性:不允许重复键,插入时会检查键是否存在。

2. 常用函数解析

#include <map>
#include <iostream>int main() {// 1. 初始化std::map<int, std::string> m1;          // 空 mapstd::map<int, std::string> m2 = {{1, "one"}, {2, "two"}};  // 列表初始化// 2. 插入与删除m1.insert({3, "three"});                // 插入键值对m1[4] = "four";                         // 使用 operator[] 插入或修改m1.erase(3);                            // 删除键为 3 的元素// 3. 查找与访问auto it = m1.find(2);                   // 查找键为 2 的元素if (it != m1.end()) {std::cout << it->second << std::endl;  // 输出 "two"}// 4. 范围查询auto lb = m1.lower_bound(1);            // 返回 >=1 的第一个元素auto ub = m1.upper_bound(2);            // 返回 >2 的第一个元素auto range = m1.equal_range(2);         // 返回 lower_bound 和 upper_bound// 5. 遍历for (const auto& [key, value] : m1) {std::cout << key << " => " << value << std::endl;}return 0;
}

3. 性能优势

  • 有序性:按键排序,适合范围查询(如 lower_bound/upper_bound)。
  • 稳定性:插入/删除操作不会使现有迭代器失效(除非删除目标节点)。
  • 适用场景:需要按键排序、范围查询或维护键值顺序的场景。

📌 三、std::unordered_map 详解

1. 核心特性

  • 哈希表实现:基于哈希函数计算键的存储位置,平均查找效率高。
  • 无序性:键值对的存储顺序与插入顺序无关。
  • 时间复杂度:平均插入/删除/查找为 O(1),最坏情况下退化为 O(n)(哈希冲突严重时)。
  • 键唯一性:不允许重复键,插入时会检查键是否存在。

2. 常用函数解析

#include <unordered_map>
#include <iostream>int main() {// 1. 初始化std::unordered_map<int, std::string> um1;  // 空 unordered_mapstd::unordered_map<int, std::string> um2 = {{1, "one"}, {2, "two"}};  // 列表初始化// 2. 插入与删除um1.insert({3, "three"});               // 插入键值对um1[4] = "four";                        // 使用 operator[] 插入或修改um1.erase(3);                           // 删除键为 3 的元素// 3. 查找与访问auto it = um1.find(2);                  // 查找键为 2 的元素if (it != um1.end()) {std::cout << it->second << std::endl;  // 输出 "two"}// 4. 哈希控制um1.reserve(100);                       // 预分配桶数量um1.max_load_factor(0.5);               // 设置最大负载因子um1.rehash(200);                        // 强制重新哈希// 5. 遍历for (const auto& [key, value] : um1) {std::cout << key << " => " << value << std::endl;}return 0;
}

3. 性能优势

  • 高效查找:哈希表实现使得平均查找效率接近 O(1)。
  • 动态扩容:通过 load_factor 和 rehash 控制哈希冲突。
  • 适用场景:高频查找、插入/删除且无需有序性的场景。

📌 四、std::map 与 std::unordered_map 对比

特性std::mapstd::unordered_map
底层实现红黑树哈希表
有序性键有序键无序
插入/删除效率O(log n)平均 O(1),最坏 O(n)
查找效率O(log n)平均 O(1),最坏 O(n)
内存占用较高(红黑树节点)较低(哈希表桶)
适用场景有序性需求、范围查询高效查找、无需有序性

📌 五、性能优化建议

1. std::map 优化

  • 利用有序性:优先使用 lower_bound/upper_bound 进行范围查询。
  • 避免频繁插入/删除:红黑树的旋转和调整可能影响性能。
  • 自定义比较函数:对于复杂键类型,提供自定义比较器以提升效率。

2. std::unordered_map 优化

  • 优化哈希函数:自定义键类型的哈希函数需满足离散性和均匀分布。
  • 控制负载因子:通过 max_load_factor 和 rehash 减少哈希冲突。
  • 预分配桶数量:使用 reserve 预分配桶,避免频繁扩容。
  • 避免 operator[] 自动插入:使用 find + insert 避免不必要的默认值插入。

📌 六、常见陷阱与解决方案

1. 哈希冲突陷阱

  • 问题:哈希冲突严重时,std::unordered_map 性能退化为 O(n)。
  • 解决方案:优化哈希函数、调整负载因子、使用 rehash
std::unordered_map<std::string, int> um;
um.max_load_factor(0.5);  // 降低负载因子以减少冲突
um.rehash(1000);          // 强制重新哈希

2. 键类型不可变陷阱

  • 问题:修改键值会导致哈希值变化,元素无法正确定位。
  • 解决方案:确保键类型不可变,或重新插入修改后的键值对。
// 错误示例:修改键值会导致元素丢失
struct MyKey {int id;bool operator==(const MyKey& o) const { return id == o.id; }
};
namespace std {
template<> struct hash<MyKey> {size_t operator()(const MyKey& k) const { return k.id; }
};
}
MyKey key = {1};
um[key] = 100;
key.id = 2;  // 修改键值,导致哈希值变化
auto it = um.find(key);  // 无法找到原键值对

3. 自定义键类型陷阱

  • 问题:未正确实现 operator== 和 std::hash 导致冲突。
  • 解决方案:确保键类型满足哈希函数的离散性和比较逻辑。
// 自定义键类型示例
struct MyKey {int id;std::string name;bool operator==(const MyKey& o) const {return id == o.id && name == o.name;}
};
namespace std {
template<> struct hash<MyKey> {size_t operator()(const MyKey& k) const {return hash<int>()(k.id) ^ (hash<std::string>()(k.name) << 1);}
};
}

📌 七、典型应用场景

1. std::map 的应用场景

  • 需要有序性:如实现字典、日志按时间排序。
  • 范围查询:如统计某个区间内的键值对。
  • 维护键值顺序:如缓存淘汰策略(LRU 需要有序性)。
// 示例:统计单词出现次数并按字母顺序输出
std::map<std::string, int> word_count;
word_count["apple"]++;
word_count["banana"]++;
word_count["cherry"]++;
for (const auto& [word, count] : word_count) {std::cout << word << ": " << count << std::endl;
}

2. std::unordered_map 的应用场景

  • 高频查找:如数据库索引、缓存映射。
  • 无需有序性:如用户登录状态存储、IP 地址计数。
  • 动态数据规模:如实时数据统计、高频交易系统。
// 示例:统计用户登录次数
std::unordered_map<std::string, int> login_count;
login_count["user1"]++;
login_count["user2"]++;
login_count["user1"]++;
for (const auto& [user, count] : login_count) {std::cout << user << " logged in " << count << " times." << std::endl;
}

🧠 总结

容器优点缺点适用场景
std::map有序性、范围查询高效插入/删除较慢,内存占用高需要有序性、范围查询
std::unordered_map查找/插入/删除高效无序性、哈希冲突影响性能高频查找、无需有序性
http://www.xdnf.cn/news/1076581.html

相关文章:

  • 解决安装UBUNTU20.04 提示尝试将SCSI(0,0,0),第一分区(sda)设备的一个vfat文件系统挂载到/boot/efi失败...问题
  • poi java设置字体样式
  • 数据结构day4——栈
  • WPF学习笔记(18)触发器Trigger
  • Cypher 是 Neo4j 专用的查询语言
  • 归因问答-有效归因实践
  • 笔记本电脑怎样投屏到客厅的大电视?怎样避免将电脑全部画面都投出去?
  • Nginx重定向协议冲突解决方案:The plain HTTP request was sent to HTTPS port
  • Qt中使用QSettings数据或结构体到INI文件
  • 用 YOLOv8 + DeepSORT 实现目标检测、追踪与速度估算
  • 05【C++ 入门基础】内联、auto、指针空值
  • 物联网数据洪流下,TDengine 如何助 ThingLinks 实现 SaaS 平台毫秒级响应?
  • 在Linux中下载docker
  • 【SQL优化案例】索引创建不合理导致SQL消耗大量CPU资源
  • SpringBoot - 定时任务改Cron不重启,调度规则生效
  • RuoYi-Vue前后端分离版实现前后端合并
  • 用Fiddler中文版抓包工具掌控微服务架构中的接口调试:联合Postman与Charles的高效实践
  • docker desktop部署本地gitlab服务
  • 学习昇腾开发的第12天--安装第三方依赖
  • 基于springboot的养老院管理系统
  • LINUX2.6设备注册与GPIO相关的API
  • Vue3 中 Excel 导出的性能优化与实战指南
  • JavaScript 安装使用教程
  • ip网络基础
  • FastGPT与MCP:解锁AI新时代的技术密码
  • 百度轮岗:任命新CFO,崔珊珊退居业务二线
  • 使用Electron开发跨平台RSS阅读器:从零到一的完整指南
  • Linux查看空间大小相关命令内容
  • 数据结构复习4
  • 前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理