C++ STL 入门:map 键值对容器
C++ STL 入门:map 键值对容器
一、核心特性与适用场景
map
是 C++ STL 提供的关联式键值容器,基于红黑树实现,具备以下核心特征:
特性 | 表现形式 | 底层原理 |
---|---|---|
键唯一性 | 不允许重复键值 | 红黑树节点键值唯一约束 |
自动排序 | 元素按键升序排列 | 红黑树中序遍历特性 |
对数复杂度 | 插入/查找/删除均为 O(log n) | 树高平衡特性保障 |
典型应用场景:
- 字典统计(如词频统计)
- 有序数据映射(如时间序列数据)
- 配置管理(键值对存储)
- 关联容器组合(与
vector
/set
配合)
二、基础操作与代码演示
1. 容器初始化
#include <iostream>
#include <map>
using namespace std;int main() {// 默认构造:空映射map<string, int> m; // 初始化列表构造(C++11)map<string, int> m2 = {{"hello", 4}, {"world", 3}, {"ilovewuhan", 5}};
2. 元素操作全流程
// 插入操作m["hello"] = 4; // 插入键值对m["world"] = 3; m["ilovewuhan"] = 5;m["ha"] = 1; // 支持中文键值(字符串编码需统一)// 下标访问(注意副作用)cout << "hello : " << m["hello"] << endl; // 输出4// 安全访问示范if(m.find("cpp") != m.end()) {cout << m.at("cpp"); // 不存在时抛出out_of_range}// 迭代器遍历for(auto it = m.begin(); it != m.end(); ++it){cout << it->first << ":" << it->second << " ";} // 输出按键排序:ha hello ilovewuhan world// 获取容器大小cout << "\nmap的长度为: " << m.size() << endl; // 输出4
三、操作时间复杂度分析
操作类型 | 时间复杂度 | 特性说明 |
---|---|---|
insert() | O(log n) | 需要平衡树结构调整 |
erase() | O(log n) | 同样涉及树重构 |
find() | O(log n) | 二分查找特性 |
operator[] | O(log n) | 不存在时自动插入默认值 |
at() | O(log n) | 带异常检查的安全访问 |
遍历 | O(n) | 中序遍历完整红黑树 |
四、进阶用法与技巧
1. 自定义排序规则
// 创建降序map(按字符串长度排序)
struct cmp {bool operator()(const string& a, const string& b) const {return a.size() < b.size(); // 按键长排序}
};
map<string, int, cmp> m3;m3["a"] = 1;
m3["longer"] = 2;
m3["mid"] = 3;// 遍历顺序:a → mid → longer
2. 批量操作优化
// 范围删除
auto start = m.lower_bound("h"); // >=h的第一个键
auto end = m.upper_bound("w"); // >w的第一个键
m.erase(start, end); // 删除所有h ≤ key < w的键值对// 合并操作(C++17)
map<string, int> src{{"x",10}, {"y",20}};
m.merge(src); // 将src中的元素移动到m中
3. 节点控制(C++17)
// 提取节点修改键
auto nh = m.extract("ilovewuhan");
nh.key() = "newkey"; // 修改键值(必须保持排序不变!)
m.insert(move(nh)); // 重新插入// 节点拼接(零拷贝转移)
map<string, int> m4;
m4.insert(m.extract(m.begin())); // 将首个节点转移到m4
五、易错点与解决方案
错误类型 | 典型表现 | 解决方案 |
---|---|---|
意外插入 | m["nonexist"] 返回0 | 优先使用find() 检查 |
排序规则错误 | 自定义比较器未满足严格弱序 | 遵循运算符重载规范 |
迭代器失效 | 遍历时删除元素 | 使用erase() 返回新迭代器 |
多线程竞争 | 并发读写未加锁 | 使用std::mutex 保护 |
安全访问示例:
int get_value(const map<string, int>& m, const string& key) {auto it = m.find(key);if(it != m.end()) return it->second;throw invalid_argument("Key not found");
}
六、性能优化技巧
- 空间预留优化(C++11)
m.max_size(); // 查询最大容量
m.rehash(100); // 预留足够哈希桶(unordered_map用法)
- 插入效率优化
// 使用emplace原地构造
m.emplace("newkey", 42); // 避免临时对象生成// 批量插入优化
vector<pair<string, int>> vec = { /*...*/ };
m.insert(vec.begin(), vec.end()); // 批量插入O(n log n)
- 内存收缩技巧
// 清空并释放内存
map<string, int>(m).swap(m); // 合并后收缩
m.merge(m2);
map<string, int>(m).swap(m);
七、知识延伸与对比
容器类型 | 键唯一性 | 查找效率 | 排序特性 | 典型应用场景 |
---|---|---|---|---|
map | 是 | O(log n) | 自动升序 | 有序键值对存储 |
unordered_map | 是 | O(1)平均 | 无序 | 快速查找 |
multimap | 否 | O(log n) | 允许重复键 | 一对多关系映射 |
set | N/A | O(log n) | 仅存储键 | 数据去重 |
配套练习项目:
LeetCode Map相关题目
CppReference map文档