开篇介绍:
hello 大家,那么在上篇博客中,我们一起学习了STL中的set和multiset容器,我们知道,它是基于key值类型的红黑树,那么我们前面学习二叉搜索树的时候也知道,对于二叉搜索树,不仅有key值类型的,还有key、value类型的,那么它对应的就是map和multimap。
OK,那么我们话不多说,下面就正式展开对map和multimap的详细介绍~
map - C++ Reference
https://legacy.cplusplus.com/reference/map/map/?kw=map
map:
一、map 核心特性铺垫
在了解函数接口前,先明确 map 的核心属性(与 set 对比,突出 key-value 特性):
- 底层是红黑树(平衡二叉搜索树),增删查效率均为 O (logN);
- 元素按 key 有序存储(默认升序),遍历顺序是红黑树的中序遍历;
- 「核心特性」key 唯一(自带去重),一个 key 对应一个 value;
- 存储的元素是
pair<const Key, T>结构体(first=key,second=value); - 迭代器是双向迭代器(仅支持
++/--,不支持随机访问); - 不可修改 key(
pair的 first 是 const 类型),但可修改 value(second 可读写),修改 key 会破坏红黑树结构。
二、构造函数(创建 map 容器)
核心作用:初始化 map 容器,存储 key-value 键值对,可指定 key 排序规则,是使用 map 的第一步。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
无参构造 map<Key, T> m; |
创建空 map,key 默认升序 | 无 | 后续插入键值对 | key 类型需支持比较(默认 less<Key>) |
map<int, string> m; m.insert({1, "apple"});(结果:{1:"apple"}) |
自定义比较 map<Key, T, Compare> m; |
按指定规则对 key 排序(如降序) | Compare 为仿函数(如 greater<Key>) |
需按 key 降序或自定义排序 | 比较规则仅作用于 key,与 value 无关;自定义仿函数需满足 "严格弱序" | map<int, string, greater<int>> m; m.insert({2:"banana", 1:"apple"});(结果:{2:"banana", 1:"apple"}) |
迭代器区间 map<Key, T> m(first, last); |
从其他 map / 键值对容器的区间 [first, last) 拷贝元素 | 源容器迭代器区间(元素需为 pair 类型) |
批量导入键值对 | 自动按 key 去重 + 排序 | vector<pair<int, string>> v={``{3:"orange"}, {2:"banana"}, {3:"pear"}}; map<int, string> m(v.begin(), v.end());(结果:{2:"banana", 3:"orange"}) |
初始化列表 map<Key, T> m({pair1, pair2,...}); |
用 {key, value} 或 make_pair(key, value) 直接初始化 |
键值对列表(支持 {key, value} 隐式转 pair) |
创建时已知所有键值对 | 最简洁的初始化方式,key 重复会自动去重 | map<int, string> m={``{1:"apple"}, {2:"banana"}, {1:"pear"}};(结果:{1:"apple", 2:"banana"}) |
拷贝构造 map<Key, T> m(other); |
复制另一个 map 的键值对和排序规则 | 已存在的 map 容器 | 复用已有 map 数据 | 原容器不变,复制后 key 仍唯一 | map<int, string> m1={``{1:"apple"}}; map<int, string> m2(m1);(m2 结果:{1:"apple"}) |
三、迭代器函数(遍历 map 元素)
核心作用:获取遍历起点 / 终点,支持正向、反向遍历,迭代器解引用后为 pair<const Key, T> 类型,需通过 first/second 访问 key/value。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
begin()/end() |
begin() 指向首键值对,end() 指向尾后位置(不指向任何元素) |
双向迭代器(map<Key,T>::iterator) |
正向遍历 | end() 不可解引用;可通过 it->first 访问 key,it->second 访问 / 修改 value |
map<int, string> m={``{1:"apple"}, 2:"banana"}; for (auto it=m.begin(); it!=m.end(); ++it) { cout << it->first << ":" << it->second; }(输出:1:apple 2:banana) |
rbegin()/rend() |
rbegin() 指向尾键值对,rend() 指向首前位置(不指向任何元素) |
反向迭代器(map<Key,T>::reverse_iterator) |
反向遍历(从后往前) | ++rit 向前移动;访问方式为 rit->first/rit->second |
map<int, string> m={``{1:"apple"}, 2:"banana"}; for (auto rit=m.rbegin(); rit!=m.rend(); ++rit) { cout << rit->first << ":" << rit->second; }(输出:2:banana 1:apple) |
cbegin()/cend() |
只读迭代器,功能同 begin()/end() |
常量双向迭代器(const_iterator) |
仅读取元素,不修改 value | 不可修改 it->second,更安全;key 始终不可修改 |
map<int, string> m={``{1:"apple"}}; for (auto it=m.cbegin(); it!=m.cend(); ++it) { cout << it->first << ":" << it->second; }(输出:1:apple) |
四、插入函数(向 map 添加键值对)
核心作用:向容器添加 pair 类型键值对,自动按 key 排序 + 去重,支持单个、批量插入。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
单个插入 pair<iterator, bool> insert(const pair<Key,T>& val); |
插入单个键值对,key 不存在则成功,存在则失败 | 返回 pair:- first:键值对迭代器;- second:插入成功标志(true/false) |
插入单个键值对 | 重复 key 插入失败,first 指向已有 key 的迭代器 |
map<int, string> m; auto ret = m.insert({1, "apple"}); cout << ret.second;(新 key 返回 true) |
位置插入 iterator insert(iterator pos, const pair<Key,T>& val); |
传入提示位置插入键值对(红黑树自动调整) | 提示迭代器 + 键值对,返回插入后迭代器 | 优化插入效率 | 提示位置不影响排序结果;重复 key 插入失败,返回已有迭代器 | map<int, string> m={``{2:"banana"}}; m.insert(m.begin(), {1, "apple"});(结果:{1:"apple", 2:"banana"}) |
批量插入(区间) insert(first, last); |
从其他键值对容器区间 [first, last) 批量插入 | 源容器迭代器区间(元素为 pair 类型) |
批量导入键值对 | 自动按 key 去重 + 排序,区间为左闭右开 | vector<pair<int, string>> v={``{3:"orange"}, {2:"banana"}}; map<int, string> m; m.insert(v.begin(), v.end());(结果:{2:"banana", 3:"orange"}) |
批量插入(列表) insert({pair1, pair2,...}); |
用 {key, value} 批量插入键值对 |
键值对列表(支持隐式转 pair) |
已知多个键值对,一次性导入 | 最简洁的批量插入方式 | map<int, string> m; m.insert({``{1:"apple"}, {2:"banana"}, {3:"orange"}});(结果:{1:"apple", 2:"banana", 3:"orange"}) |
五、删除函数(从 map 移除键值对)
核心作用:按 key、位置或区间删除键值对,删除后红黑树自动平衡,删除依据是 key。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
按位置删除 iterator erase(iterator pos); |
删除迭代器指向的键值对,返回下一个迭代器 | 键值对迭代器 | 已知键值对位置(如 find 找到) | 不可传入 end() 迭代器;仅删除指定键值对 |
map<int, string> m={``{1:"apple"}, 2:"banana"}; auto it=m.find(1); if (it!=m.end()) m.erase(it);(结果:{2:"banana"}) |
按 key 删除 size_type erase(const Key& key); |
删除 key 对应的键值对,返回删除个数(0 或 1) | 要删除的 key 值 | 知道 key,直接删除对应键值对 | key 不存在返回 0;仅删除匹配 key 的唯一键值对 | map<int, string> m={``{1:"apple"}, 2:"banana"}; int num = m.erase(1); cout << num;(输出:1,结果:{2:"banana"}) |
按区间删除 erase(first, last); |
删除区间 [first, last) 内的所有键值对 | 迭代器区间 | 批量删除连续键值对 | 左闭右开区间,可结合 lower_bound/upper_bound 精准定位 |
map<int, string> m={``{1:"apple"}, 2:"banana", 3:"orange"}; m.erase(m.lower_bound(2), m.upper_bound(3));(结果:{1:"apple"}) |
清空 clear(); |
删除所有键值对,清空容器 | 无 | 需清空整个 map | 清空后 size() 为 0,empty() 返回 true |
map<int, string> m={``{1:"apple"}}; m.clear(); cout << m.empty();(输出:true) |
六、查找函数(查询 map 中的键值对)
核心作用:按 key 查询,支持精准查找、范围查找,基于红黑树实现,效率极高。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
精准查找 iterator find(const Key& key); |
按 key 查找,返回对应键值对迭代器(不存在返回 end()) |
要查找的 key 值 | 确认 key 是否存在并定位 | 效率 O (logN),远优于遍历查找;仅按 key 匹配,与 value 无关 | map<int, string> m={``{1:"apple"}}; auto it=m.find(1); if (it!=m.end()) cout << it->second;(输出:apple) |
统计个数 size_type count(const Key& key); |
统计 key 对应的键值对个数(0 或 1) | 要统计的 key 值 | 快速判断 key 是否存在 | key 唯一,返回 1 表示存在,0 表示不存在 | map<int, string> m={``{1:"apple"}}; if (m.count(1)) cout << "key 1 存在";(输出:key 1 存在) |
下界 lower_bound(const Key& key); |
查找第一个 key ≥ 目标 key 的键值对迭代器 | 目标 key 值 | 定位 key 的起始范围 | 无匹配 key 返回 end() |
map<int, string> m={``{1:"apple"}, 3:"orange"}; auto it=m.lower_bound(2);(返回 3:"orange" 的迭代器) |
上界 upper_bound(const Key& key); |
查找第一个 key > 目标 key 的键值对迭代器 | 目标 key 值 | 定位 key 的结束范围 | 无匹配 key 返回 end() |
map<int, string> m={``{1:"apple"}, 3:"orange"}; auto it=m.upper_bound(1);(返回 3:"orange" 的迭代器) |
范围查找 equal_range(const Key& key); |
返回 key 的下界和上界迭代器对(first = 下界,second = 上界) | 目标 key 值,返回 pair<iterator, iterator> |
快速获取 key 对应的范围(适配 multimap) | map 中范围长度为 0 或 1,遍历区间可获取匹配键值对 | map<int, string> m={``{2:"banana"}}; auto range=m.equal_range(2); cout << range.first->second;(输出:banana) |
七、其他常用函数
核心作用:辅助操作 map 容器,补充核心接口功能。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
size() |
返回键值对总个数 | 非负整数(size_type 本质是 unsigned int) |
知道 map 中元素数量 | 不可用于判断空容器,推荐用 empty() |
map<int, string> m={``{1:"apple"}, 2:"banana"}; cout << m.size();(输出:2) |
empty() |
判断容器是否为空(无键值对) | bool 类型(true = 空,false = 非空) | 避免对空容器操作 | 比 size() == 0 更高效 |
map<int, string> m; if (m.empty()) cout << "空容器";(输出:空容器) |
key_comp() |
获取 key 的比较对象(如 less<Key>/greater<Key>) |
返回比较仿函数(key_compare) |
复用 key 的排序规则 | 比较的是键值对的 key 部分 | map<int, string> m; auto comp=m.key_comp(); bool res=comp(1,2);(less 返回 true) |
value_comp() |
按 key 比较键值对(比较的是 pair 的 key 部分) |
返回比较仿函数(value_compare) |
按 key 比较两个键值对 | 与 key_comp() 逻辑一致,但参数是 pair 类型 |
map<int, string> m={``{1:"a"}, 2:"b"}; auto comp=m.value_comp(); bool res=comp(*m.begin(), *m.rbegin());(less 返回 true) |
swap(map& x); |
交换两个 map 的键值对和排序规则 | 另一个同类型 map 容器 | 快速交换数据 | 效率高于手动拷贝,交换后原容器元素替换为对方元素 | map<int, string> m1={``{1:"a"}}, m2={``{2:"b"}}; m1.swap(m2);(m1 结果:{2:"b"}) |
八、[] 运算符重载(访问 / 修改 value,自动插入 key)
核心作用:通过 key 快速访问或修改对应的 value,若 key 不存在则自动插入该 key(value 为默认构造值),是 map 最常用的便捷接口。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | |
|---|---|---|---|---|---|
T& operator[](const Key& key); |
1. 若 key 存在:返回对应 value 的左值引用(可修改);2. 若 key 不存在:自动插入{key, T()}(value 为默认值,如 int=0、string=""),并返回新 value 的左值引用。 |
参数:要访问的 key;返回值:value 的左值引用(T&)。 |
快速访问 / 修改 value,允许自动插入新 key | 1. 必然触发 "不存在则插入",可能导致意外的 key 插入;2. 仅能修改 value,key 仍不可改;3. 需确保 value 类型支持默认构造(如 int、string,自定义类型需提供默认构造函数)。 |
### 关键细节补充
#### 1. 与`insert`的区别
- `[]`:更侧重"访问/修改",**无条件插入不存在的key**,返回value引用;
- `insert`:更侧重"插入",**key存在则插入失败**,返回`pair<iterator, bool>`(可判断是否插入成功)。
示例对比:
```cpp
map<int, string> m;
m.insert({1, "apple"}); // key=1不存在,插入成功
m.insert({1, "red apple"}); // key=1已存在,插入失败(m中仍为"apple")
m[1] = "red apple"; // 直接修改value,覆盖为"red apple"
m[2] = "banana"; // 自动插入key=2,value为"banana"
2. 与find的区别
[]:必然访问 value,不存在则插入;find:仅查找 key ,不存在则返回end()(不插入),更适合 "仅判断 key 是否存在" 的场景。
示例对比:
map<int, string> m{{1, "apple"}};
auto it = m.find(2); // key=2不存在,返回m.end()(无插入)
if (it == m.end()) cout << "key=2不存在"; // 输出:key=2不存在
m[2]; // 无操作,但已自动插入{2, ""},m.size()变为2
3. 常量 map 不可用[]
const map中无法使用operator[],因为它可能修改 map(插入新 key),此时需用at()(C++11+)或find:
const map<int, string> m{{1, "apple"}};
// m[1]; // 编译错误:const map不允许修改/插入
cout << m.at(1); // 安全访问:key存在返回value,不存在抛out_of_range异常
总结(含 [] 的高频接口补充)
map 的核心接口可整合为:
- 便捷访问 / 修改 :
[](快速但可能插入)、at()(安全访问,C++11+); - 插入 :
insert()(可控插入,返回结果)、emplace()(原地构造,更高效,C++11+); - 查找 :
find()(精准定位)、count()(判断存在); - 删除 :
erase(key)(按 key 删)、erase(it)(按位置删);
其中[]是 "双刃剑"------ 便捷但需警惕意外插入,若仅需 "查找不插入",优先用find;若需 "访问并允许修改",用[]更高效。
总结
map 的函数接口围绕 "key 有序唯一、key-value 映射、高效操作" 设计,核心高频接口:
- 构造:初始化列表构造(
{``{key1, val1}, ...}最方便); - 遍历:
begin()/end()+ 范围 for(for (auto& p : m) { p.second = ...; }可修改 value); - 增删:
insert({key, val})(单个 / 批量)、erase(key)(按 key 删)、erase(it)(按位置删); - 查找:
find(key)(精准定位)、count(key)(判断存在)、lower_bound()/upper_bound()(范围定位)。
multimap:
一、multimap 核心特性铺垫
在了解函数接口前,先明确 multimap 的核心属性(与 map 对比,突出 key 可重复):
- 底层是红黑树(平衡二叉搜索树),增删查效率均为 O (logN);
- 元素按 key 有序存储(默认升序),遍历顺序是红黑树的中序遍历;
- 「核心差异」key 可重复,一个 key 可对应多个 value;
- 存储的元素是
pair<const Key, T>结构体(first=key,second=value); - 迭代器是双向迭代器(仅支持
++/--,不支持随机访问); - 不可修改 key(
pair的 first 是 const 类型),但可修改 value(second 可读写)。
二、构造函数(创建 multimap 容器)
核心作用:初始化 multimap 容器,用法与 map 完全一致,仅差异在于 "key 可重复,无去重逻辑"。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
无参构造 multimap<Key, T> m; |
创建空 multimap,key 默认升序 | 无 | 后续插入键值对 | key 可重复,默认用 less<Key> 排序 |
multimap<int, string> m; m.insert({1:"apple"}); m.insert({1:"pear"});(结果:{1:"apple", 1:"pear"}) |
自定义比较 multimap<Key, T, Compare> m; |
按指定规则对 key 排序(如降序) | Compare 为仿函数(如 greater<Key>) |
需按 key 降序或自定义排序 | 重复 key 按排序规则连续存储;自定义仿函数需满足 "严格弱序" | multimap<int, string, greater<int>> m; m.insert({2:"banana", 1:"apple", 2:"pear"});(结果:{2:"banana", 2:"pear", 1:"apple"}) |
迭代器区间 multimap<Key, T> m(first, last); |
从其他键值对容器区间 [first, last) 拷贝元素 | 源容器迭代器区间(元素为 pair 类型) |
批量导入键值对 | 不自动去重,原样拷贝 + 按 key 排序 | vector<pair<int, string>> v={``{3:"orange"}, {2:"banana"}, {2:"pear"}}; multimap<int, string> m(v.begin(), v.end());(结果:{2:"banana", 2:"pear", 3:"orange"}) |
初始化列表 multimap<Key, T> m({pair1,...}); |
用 {key, value} 直接初始化 |
键值对列表(支持隐式转 pair) |
创建时已知所有键值对 | 重复 key 会保留,按 key 排序 | multimap<int, string> m={``{1:"apple"}, {2:"banana"}, {1:"pear"}};(结果:{1:"apple", 1:"pear", 2:"banana"}) |
拷贝构造 multimap<Key, T> m(other); |
复制另一个 multimap 的键值对和排序规则 | 已存在的 multimap 容器 | 复用已有 multimap 数据 | 原容器不变,复制后保留 key 重复特性 | multimap<int, string> m1={``{1:"a"}, 1:"b"}; multimap<int, string> m2(m1);(m2 结果:{1:"a", 1:"b"}) |
三、迭代器函数(遍历 multimap 元素)
核心作用:与 map 完全一致,迭代器解引用后为 pair<const Key, T>,支持正向、反向遍历,仅遍历结果包含重复 key 的键值对。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
begin()/end() |
begin() 指向首键值对,end() 指向尾后位置 |
双向迭代器 | 正向遍历 | 可修改 it->second;end() 不可解引用 |
multimap<int, string> m={``{1:"a"}, 1:"b"}; for (auto it=m.begin(); it!=m.end(); ++it) { cout << it->first << ":" << it->second; }(输出:1:a 1:b) |
rbegin()/rend() |
rbegin() 指向尾键值对,rend() 指向首前位置 |
反向迭代器 | 反向遍历 | ++rit 向前移动;重复 key 逆序排列 |
multimap<int, string> m={``{1:"a"}, 1:"b"}; for (auto rit=m.rbegin(); rit!=m.rend(); ++rit) { cout << rit->first << ":" << rit->second; }(输出:1:b 1:a) |
cbegin()/cend() |
只读迭代器,功能同 begin()/end() |
常量双向迭代器 | 仅读取元素,不修改 value | 不可修改 it->second,key 始终不可修改 |
multimap<int, string> m={``{1:"a"}}; for (auto it=m.cbegin(); it!=m.cend(); ++it) { cout << it->first << ":" << it->second; }(输出:1:a) |
四、插入函数(向 multimap 添加键值对)
核心作用:与 map 类似,但 key 可重复,插入接口返回值无 bool 标志。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
单个插入 iterator insert(const pair<Key,T>& val); |
插入单个键值对,key 可重复,自动按 key 排序 | 键值对(pair 类型),返回插入后迭代器 |
插入单个键值对 | 无去重逻辑,重复 key 正常插入;返回新插入键值对的迭代器 | multimap<int, string> m; auto it=m.insert({1:"a"}); m.insert({1:"b"});(结果:{1:"a", 1:"b"}) |
位置插入 iterator insert(iterator pos, const pair<Key,T>& val); |
传入提示位置插入键值对(红黑树自动调整) | 提示迭代器 + 键值对,返回插入后迭代器 | 优化插入效率 | 提示位置不影响排序结果;重复 key 仍可插入 | multimap<int, string> m={``{2:"b"}}; m.insert(m.begin(), {1:"a"}); m.insert(m.begin(), {1:"c"});(结果:{1:"a", 1:"c", 2:"b"}) |
批量插入(区间) insert(first, last); |
从其他键值对容器区间 [first, last) 批量插入 | 源容器迭代器区间 | 批量导入键值对 | 不自动去重,保留所有重复 key,仅按 key 排序 | vector<pair<int, string>> v={``{3:"c"}, 2:"b", 2:"d"}; multimap<int, string> m; m.insert(v.begin(), v.end());(结果:{2:"b", 2:"d", 3:"c"}) |
批量插入(列表) insert({pair1, pair2,...}); |
用 {key, value} 批量插入键值对 |
键值对列表 | 已知多个键值对,一次性导入 | 最简洁的批量插入方式,重复 key 正常保留 | multimap<int, string> m; m.insert({``{1:"a"}, {2:"b"}, {1:"c"}});(结果:{1:"a", 1:"c", 2:"b"}) |
五、删除函数(从 multimap 移除键值对)
核心作用:与 map 类似,但按 key 删除时会删除所有重复 key 的键值对,返回实际删除个数。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
按位置删除 iterator erase(iterator pos); |
删除迭代器指向的单个键值对,返回下一个迭代器 | 键值对迭代器 | 已知单个键值对位置 | 仅删除指定键值对,不影响其他重复 key 的元素;不可传入 end() |
multimap<int, string> m={``{1:"a"}, 1:"b"}; auto it=m.find(1); m.erase(it);(结果:{1:"b"}) |
按 key 删除 size_type erase(const Key& key); |
删除所有 key 匹配的键值对,返回删除个数 | 要删除的 key 值 | 需删除所有重复 key 的元素 | key 不存在返回 0;存在则删除全部重复项,返回删除个数(≥1) | multimap<int, string> m={``{1:"a"}, 1:"b"}; int num=m.erase(1); cout << num;(输出:2,结果:空) |
按区间删除 erase(first, last); |
删除区间 [first, last) 内的所有键值对 | 迭代器区间 | 批量删除连续键值对(含重复 key) | 左闭右开区间,可结合 lower_bound/upper_bound 精准删除重复 key 范围 |
multimap<int, string> m={``{1:"a"}, 1:"b", 2:"c"}; auto first=m.lower_bound(1); auto last=m.upper_bound(1); m.erase(first, last);(结果:{2:"c"}) |
清空 clear(); |
删除所有键值对,清空容器 | 无 | 需清空整个 multimap | 清空后 size() 为 0,empty() 返回 true |
multimap<int, string> m={``{1:"a"}}; m.clear(); cout << m.empty();(输出:true) |
六、查找函数(查询 multimap 中的键值对)
核心作用:与 map 类似,但因 key 可重复,查找结果和用法有差异。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
精准查找 iterator find(const Key& key); |
按 key 查找,返回中序遍历第一个匹配 key 的键值对迭代器(不存在返回 end()) |
要查找的 key 值 | 定位第一个匹配 key 的元素 | 仅返回第一个匹配项,需遍历后续元素获取所有重复 key 的键值对 | multimap<int, string> m={``{1:"a"}, 1:"b"}; auto it=m.find(1); cout << it->second;(输出:a) |
统计个数 size_type count(const Key& key); |
统计 key 对应的键值对总个数(≥0) | 要统计的 key 值 | 快速获取 key 的重复次数 | 区别于 map 的 0/1,返回实际重复次数(如 key 重复 3 次则返回 3) | multimap<int, string> m={``{1:"a"}, 1:"b", 1:"c"}; cout << m.count(1);(输出:3) |
下界 lower_bound(const Key& key); |
查找第一个 key ≥ 目标 key 的键值对迭代器 | 目标 key 值 | 定位重复 key 的起始范围 | 无匹配 key 返回 end();重复 key 中返回第一个匹配项的迭代器 |
multimap<int, string> m={``{1:"a"}, 1:"b", 2:"c"}; auto it=m.lower_bound(1);(返回 1:"a" 的迭代器) |
上界 upper_bound(const Key& key); |
查找第一个 key > 目标 key 的键值对迭代器 | 目标 key 值 | 定位重复 key 的结束范围 | 无匹配 key 返回 end();重复 key 中返回最后一个匹配项的下一个迭代器 |
multimap<int, string> m={``{1:"a"}, 1:"b", 2:"c"}; auto it=m.upper_bound(1);(返回 2:"c" 的迭代器) |
范围查找 equal_range(const Key& key); |
返回 key 的下界和上界迭代器对(first = 下界,second = 上界) | 目标 key 值,返回 pair<iterator, iterator> |
快速获取所有重复 key 的键值对(最常用) | 遍历 first 到 second 即可获取该 key 的所有 value |
multimap<int, string> m={``{1:"a"}, 1:"b"}; auto range=m.equal_range(1); for (auto it=range.first; it!=range.second; ++it) { cout << it->second; }(输出:a b) |
七、其他常用函数
核心作用:与 map 完全一致,辅助操作容器,无差异。
| 函数形式 | 功能说明 | 参数 / 返回值 | 适用场景 | 注意点 | 示例代码 |
|---|---|---|---|---|---|
size() |
返回键值对总个数(含重复 key) | 非负整数(size_type 本质是 unsigned int) |
知道 multimap 中元素总数量 | 不可用于判断空容器,推荐用 empty() |
multimap<int, string> m={``{1:"a"}, 1:"b"}; cout << m.size();(输出:2) |
empty() |
判断容器是否为空(无键值对) | bool 类型(true = 空,false = 非空) | 避免对空容器操作 | 比 size() == 0 更高效 |
multimap<int, string> m; if (m.empty()) cout << "空容器";(输出:空容器) |
key_comp() |
获取 key 的比较对象(如 less<Key>/greater<Key>) |
返回比较仿函数(key_compare) |
复用 key 的排序规则 | 比较的是键值对的 key 部分 | multimap<int, string> m; auto comp=m.key_comp(); bool res=comp(1,2);(less 返回 true) |
value_comp() |
按 key 比较键值对(比较的是 pair 的 key 部分) |
返回比较仿函数(value_compare) |
按 key 比较两个键值对 | 与 key_comp() 逻辑一致,参数是 pair 类型 |
multimap<int, string> m={``{1:"a"}, 2:"b"}; auto comp=m.value_comp(); bool res=comp(*m.begin(), *m.rbegin());(less 返回 true) |
swap(multimap& x); |
交换两个 multimap 的键值对和排序规则 | 另一个同类型 multimap 容器 | 快速交换数据 | 效率高于手动拷贝,交换后保留 key 重复特性 | multimap<int, string> m1={``{1:"a"}}, m2={``{2:"b"}}; m1.swap(m2);(m1 结果:{2:"b"}) |
总结
multimap 的函数接口与 map 高度一致,核心差异围绕 "key 可重复" 展开:
- 核心特性:key 有序、支持重复、key-value 映射、红黑树底层(O (logN) 效率);
- 关键差异接口:
- 插入:单个插入返回
iterator(无 bool 标志),重复 key 可正常插入; - 删除:按 key 删除返回实际删除个数(≥1),删除所有重复 key 的键值对;
- 查找:
find返回第一个匹配 key 的迭代器,count返回 key 重复次数,equal_range是获取所有重复 key 键值对的最优方式;
- 插入:单个插入返回
- 高频接口:与 map 一致,重点记忆
insert({key, val})(批量 / 单个)、erase(key)(按 key 删所有)、find()+equal_range()(处理重复 key)。
使用场景:需要 key 有序且允许重复、一个 key 对应多个 value 的场景(如保存多个相同优先级的任务、统计多组关联数据),无需手动处理 key 的排序和重复存储逻辑。
示例代码:
cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <functional>
#include <map>
using namespace std;
//然后就是map的使用,它和set的区别就是它是key、value类型的二叉搜索树
//即它的各种操作都是围绕着key关键值进行的
//但是每个节点里面会包含着value类型,一个key值对应着一个value
//那么要是multimap的话,那就是一个key值对应着多个value
//毕竟它可以存储多个相同的key值
//map的大部分还是和set类似的,比如去重性和排序性
//但是它有一个很大很大的区别就是,它不能简单的插入数据
//要插入的是pair结构体模版变量
//map里面的节点类型,其实就是pair结构体哦
//具体的看下面的示例代码
void Test_Map_Construct()
{
//map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,
//set默认要求Key支持小于比较,如果不支持或者需要的话可以自行实现仿函数传给第二个模版参数,
//map底层存储数据的内存是从空间配置器申请的。
//一般情况下,我们都不需要传后两个模版参数。
//map底层是用红黑树实现,增删查改效率是 ,迭代器遍历是走O(logN) 的中序,所以是按key有序顺序遍历的。
//template < class Key, // map::key_type
//class T, // map::mapped_type
// class Compare = less<Key>, // map::key_compare
// class Alloc = allocator<pair<const Key, T> > //
// map::allocator_type
// class map;
//那么很显然,因为map是key、value类型的二叉搜索树
//所以我们既要传入key值的类型,也要传入value值的类型
//这里注意:两个类型可以是不同的哦
map<int, int> m1;//无参构造,升序排列,key和value都是int类型
map<int, string> m2;//无参构造,升序排列,key和value类型不同
map<int, int, greater<int>> m3;//无参构造,降序排列,key和value都是int类型
//这里强调再强调,map的仿函数使用是默认使用key值的比较的
//那么如果我们想使用value值的比较的话,
//那么就要我们自己实现value值的比较的仿函数
//并传入map的创建和初始化,这一点需要注意。
//上面说了,要给map容器传入数据的话,就是传入pair结构体模版类型的变量进去
//那么一般是默认pair的first变量是指key值
//而pair的second变量是指value值
//记得之间的类型也要对应哦
//创建并初始化pair变量
pair<int, int> p1(1, 10);
pair<int, int> p2 = {2, 20};
//map<int, int> m4(p1, p2);
//此时不用{}也能直接传入多个pair变量
map<int, int> m4({ p1,p2 });
//当然,要加上{}也可以
//我还是推荐使用加上{}的
//同样的,也可以使用匿名对象
map<int, int> m5(pair<int, int>(1, 10), pair<int, int>(2, 20));
//那么要是说老是写pair<int, int>太烦,没事
//C++提供了make_pair函数模版
//template <class T1, class T2>
//pair<T1, T2> make_pair(T1 x, T2 y);
map<int, int> m6({ make_pair(1, 10), make_pair(2, 20) });
//它可以自动识别数据类型,也可以我们传入参数指定
map<int, int> m7({ make_pair<int,int>(1, 10), make_pair<int,int>(2, 20) });
//当然,其实不难发现,并没有方便了多少
//所以,又可以请出C++11大哥了
//依旧是使用初始化列表
map<int, int> m8({ { 1,10 }, { 2,20 }, { 3,30 } });
//这个时候就要加上一个最大的{}了哦
//那么此时花括号内的第一个数,就是指key值
//而花括号内的第二个数,就是指value值
map<int, int> m9 = { { 1,10 }, { 2,20 }, { 3,30 } };
//很显然,如上才是最方便的
map<int, string> m10 = { {1,"apple"},{2,"banana"},{3,"orange"} };
//方便!!!!!!!!!!
//拷贝构造函数
map<int, string> m11 = m10;
//赋值运算符重载函数:
m11 = m10;
//map的支持正向和反向迭代遍历,遍历默认按key的升序顺序,
//因为底层是二叉搜索树,迭代器遍历走的中序;
//支持迭代器就意味着支持范围for,map支持修改value数据,
//不支持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
//那么这个时候问题就是,诶,我怎么既得到map的key值
//也得到map的value值呢?
//不错,还是依靠pair结构体模版
//那么一般是默认pair的first变量是指key值
//而pair的second变量是指value值
//但是我们是使用迭代器诶,得到的结果是结构体诶,这可怎么办呢
//其实很简单很简单,map里面的节点类型,其实就是pair结构体哦
//所以我们把迭代器*解引用之后,得到类型就是pair类型哦
//那么我们就能通过*迭代器.first得到key值
//通过*迭代器.second得到value值
//当然,我们也可以不解引用的
//直接使用迭代器->first/second就行
//毕竟是用对->进行运算符重载的,这一点需要注意
map<int, string> m12 = { {1,"apple"},{2,"banana"},{3,"orange"},{4,"watermalen"} };
map<int, string>::iterator it = m12.begin();
while (it != m12.end())
{
cout << "key((*it).firs): " << (*it).first << endl;//注意.的优先级高于*哦
cout << "value((*it).second): " << (*it).second << endl;//注意.的优先级高于*哦
++it;//map的迭代器也是只支持++和--
}
map<int, string>::iterator it1 = m12.begin();
while (it1 != m12.end())
{
cout << "key((it1->first): " << it1->first << endl;//注意.的优先级高于*哦
cout << "value((it1->second): " << it1->second << endl;//注意.的优先级高于*哦
++it1;//map的迭代器也是只支持++和--
}
//那么map的构造和遍历也就差不多了
}
void Test_Map_Modifiers()
{
//那么搞定了map的构造和遍历的一些操作之后
//接下来就是关注map的增删查这几个操作了,那么其实它们的使用和set是差不多的
//我们先来看map容器的insert函数接口
//这个其实就是要注意,因为map是key、value类型的
//所以我们可不能就直接传入一个key进去
//而是要把key和对应的value一起传进去的
//那么上面也说了,得使用pair结构体模版
//所以本质上就是插入pair结构体模版变量
//那么它是支持只插入pair变量即可,返回的也是一个pair变量
//是iterator、bool类型的,那么我们使用这一个接口就行了,绰绰有余
map<int, int> m1;
pair<int, int> p1(1, 10);
pair<int, int> p2 = { 2, 20 };
m1.insert(p1);//一次只支持插入一个变量哦
//同样的,也可以使用匿名变量
m1.insert(pair<int, int>(2, 02));
//那么毋庸置疑的,肯定也可以使用大括号,隐式转换直接秒了好吧,嘎嘎好使
m1.insert({ 3, 30 });
for (auto m : m1)
{
cout << m.first << " ";
cout << m.second << " ";//需要注意,使用auto循环的话,可就不能使用箭头了哦,得用.
}
cout << endl << endl;
//然后就是erase函数,这个也是简单的没边了
//它和set一样,支持删除指定迭代器的数据
//也支持删除指定key值的数据哦,还是很简单的
m1.erase(m1.begin());
m1.erase(2);
//然后就是最重头戏的一个了,[]的运算符重载
//mapped_type& operator[] (const key_type& k);
//mapped_type:The second template parameter (T)
//看到原型可以看出,是要求我们传入要查找或者巴拉巴拉的key值
//然后呢,返回指定key值在map变量中的所对应的value值的引用
//注意,因为是引用,所以这也就代表着我们可以对它进行修改
//这便达到了修改指定key值的value值的目的,嘎嘎好使
//但是需要注意的是,可不要傻傻的直接就创建变量去接收[]的返回值
//然后指望着直接就修改该变量就能修改map中所对应的value值
//这是错误的想法,因为虽然返回值是引用类型,但是由于我们是用值类型去接收
//所以编译器会自动创建一个副本接收,那么我们嘎嘎修改返回值就相当于是对副本修改,那就是无用功
//所以我们想要真正的修改map中对应的value值的话,就得用引用类型的对应变量去接收才行
//然后此时直接对这个变量进行操作才能真正的改变到map中的对应的value值
//这个点很容易忽略掉,一定要注意
// 一、先明确m1[key]的返回值本质
// map的operator[]返回的是 **value的左值引用(T&)**,这个引用直接指向map内部存储的pair<const Key, T>中的second(即真正的value数据)。
// 二、为什么 "值接收" 无法修改原数据?
// 如果用auto value1 = m1[key];(值接收):
// 这里发生了值拷贝:m1[key]返回的引用会被解引用,把map内部的value值复制一份到局部变量value1中。
// value1是独立的局部变量,和map内部的原始value没有任何关联。修改value1只会改变这个副本,原map中的数据自然不会变。
// 通俗比喻:这就像你从仓库(map)里拿了一个商品(value)的照片(副本),在照片上涂鸦(修改value1),仓库里的原商品不会有任何变化。
// 三、为什么 "引用接收" 能修改原数据?
// 如果用auto& value1 = m1[key];(引用接收):
// 这里是引用绑定:value1作为引用,直接 "绑定" 到map内部存储的原始value上(相当于给原value起了个别名)。
// 对value1的修改,本质就是对map内部原始value的直接操作,自然能改变原数据。
// 通俗比喻:这就像你拿到了仓库里原商品的 "操作权限",直接对仓库里的商品修改,原商品会真的变化。
//那么其实[]的运算符重载的功能不止上面所说的修改指定key值的value值
//如果我们传入的key值并不在我们指定的map变量里面呢?
//那么这个时候会怎么处理呢?
//If k does not match the key of any element in the container,
//the function inserts a new element with that key and returns a reference to its mapped value.
//是的,如果不存在,那么该函数会帮助我们直接把我们所指定的key值插入调用它的map变量中
//然后依旧是返回这个key值所对应的value值,只不过是为空
//等待我们我们对返回值进行赋值使其有意义
//要是我们不赋值,那么该value就使用value类型的构造函数
//所以说,这个[]的本事还是可以的
auto value1 = m1[3];//使用auto比较省事
value1 = 33;//修改m1变量中key值为3所对应的value值为33
//像什么这样子是大错特错的哦,实际上是对编译器生成的返回值副本修改
//并不能真正的改变到map中对应的value值身上
//所以我们得用&引用类型的返回值接收才行哦
//1. auto value1 = m1[3]; +value1 = 33; ------ 无法修改 map 中的值
//m1[3]确实会返回 map 中 key = 3 对应 value 的引用,
//但你用auto value1接收时,auto会推导为值类型(比如int),
//相当于把m1[3]的值拷贝到value1中。此时value1是一个独立的变量,
//修改它并不会影响 map 里的原始值。
auto& value1 = m1[3];
value1 = 33;
//这样子才算是真正的修改
int value2 = m1[5];
value2 = 50;
//2. int value2 = m1[5]; +value2 = 50; ------ 插入 key = 5 但 value 未被修改
//m1[5]会自动插入 key = 5,对应的 value 是int的默认值(0),但int value2 = m1[5];
//同样是值拷贝,后续value2 = 50只修改了局部变量value2,map 中 key = 5 的 value 仍然是 0。
int& value2 = m1[5];//传入m1变量中不存在的key值,那么会帮我们自动插入该key值
value2 = 50;//修改m1变量中key值为5所对应的value值为50
for (auto m : m1)
{
cout << m.first << " ";
cout << m.second << " ";//需要注意,使用auto循环的话,可就不能使用箭头了哦,得用.
}
cout << endl << endl;
//还是比较简单的说实话
}
void Test_Map_Operations()
{
//然后这一部分主要就是对find函数接口的使用
//至于upper_bound和lower_bound的使用,在set那里已经足够详细了
//那么其实map函数的find函数也比较简单
//就是我们传入指定的key值,那么就会返回该key值所对应的迭代器
//iterator find(const key_type & k);
//const_iterator find(const key_type & k) const;
//那么要是没找到我们传入的key值的话,它就会返回end()迭代器,需要注意
//还是很简单的
map<int, int> m1({ { 1,10 }, { 2,20 }, { 3,30 } });
auto it = m1.find(2);
//然后就是count函数,那么它的使用也是和set的一模一样,所以这里就不再赘述
int count = m1.count(1);
// 返回大于等k位置的迭代器
//iterator lower_bound(const key_type & k);
// 返回大于k位置的迭代器
//const_iterator upper_bound(const key_type & k) const;
}
void Test_MultiMap()
{
//然后就是multimap的使用,其实和multiset与set的区别差不多
//multimap就是支持插入多个相同的key值的二叉搜索树罢了
//但是其对于map而言
//比如find时,有多个key,返回中序第一个。
//其次就是multimap不支持[],因为支持key冗余,[]就只能支持插入了,不能支持修改。
//multimap 允许多个元素拥有相同 key,
//此时用 [] 访问会出现歧义 ------ 编译器无法确定你要操作的是哪个相同 key 对应的 value
//(是第一个?最后一个?还是某个特定位置的?)
//所以在multiamp中是不提供[]的哦
//这点需要注意,其他的就没什么了
//还有就是所谓的equal_range,这个也是和multiset的使用一样
//同样不废话
multimap<int, int> mm1 = { {1,10},{1,11} };
for (auto m : mm1)
{
cout << m.first << " ";
cout << m.second << " ";//需要注意,使用auto循环的话,可就不能使用箭头了哦,得用.
}
cout << endl << endl;
}
int main()
{
Test_Map_Construct();
Test_Map_Modifiers();
Test_Map_Operations();
Test_MultiMap();
return 0;
}
结语:以键值为桥,探 STL 映射之妙
哈喽各位小伙伴,当你敲完 Test_MultiMap 函数的最后一个分号,咱们关于 map 和 multimap 的学习也正式落下帷幕啦~ 回想之前咱们啃完 set 和 multiset,再到今天吃透这两个 "key-value" 型容器,是不是能明显感觉到 C++ STL 的学习就像滚雪球 ------ 越往后学,越能发现不同容器间的相通逻辑,越能体会到底层设计思想的精妙。今天咱们不仅掌握了 map 和 multimap 的用法,更摸到了 "键值映射" 这种编程思维的核心,这可比单纯记几个 API 有价值多啦~
咱们先来好好复盘一下,这篇博客里咱们到底收获了哪些 "硬核干货"。map 和 multimap,本质上都是红黑树封装而来的 "有序键值对容器",红黑树赋予它们 O (logN) 的高效增删查能力,中序遍历带来的有序性让数据处理更省心。但两者的核心差异,其实就聚焦在 "key 是否唯一" 这一点上:map 的 key 自带去重特性,一个 key 只能绑定一个 value,就像现实中的 "身份证对应人"------ 唯一且精准;而 multimap 打破了 key 的唯一性限制,一个 key 能对应多个 value,好比 "一个班级对应多个学生"------ 灵活且包容。
这种差异直接决定了它们的接口设计和使用场景。咱们学 map 的时候,印象最深的肯定是pair结构体和[]运算符重载吧?map 存储的每一个元素都是pair<const Key, T>,first 绑定不可修改的 key,second 对应可读写的 value,这种结构让 "键值映射" 变得直观又清晰。而[]运算符更是 map 的 "灵魂操作"------ 既能快速插入新键值对,又能直接修改已有 key 对应的 value,嘎嘎方便,但背后的逻辑咱们可不能忘:当 key 不存在时,它会自动插入一个默认构造的 value,这也是 map 独有的便捷特性。
再看 multimap,它和 map 就像 "孪生兄弟",大部分接口用法完全一致,但因为支持 key 重复,又有自己的 "小个性":比如它没有[]运算符重载(毕竟一个 key 对应多个 value,编译器没法确定你要操作哪个);比如 insert 插入时不会返回 bool 标志(反正 key 可重复,插了就成功);比如按 key 删除时会删掉所有重复的 key,count 函数返回的是 key 的实际重复次数。而equal_range函数在 multimap 里更是 "大显身手",一个接口就能拿到某个 key 对应的所有 value 的范围,比手动遍历判断高效多了,这也是处理多值映射的最优解。
咱们在示例代码里其实也能发现,学习 map 和 multimap 从来不是 "孤立记接口",而是要结合 "底层逻辑" 和 "实际场景"。比如为什么 map 的 key 不能修改?因为 key 是红黑树排序的依据,改了 key 就会破坏树的平衡;为什么 multimap 不支持[]?因为多值映射下的[]会有歧义;为什么两者的增删查效率都这么高?因为红黑树的平衡机制在默默兜底。理解了这些底层逻辑,那些看似零散的接口(比如 insert 的返回值差异、erase 的删除规则)就都能串联起来,不用死记硬背也能灵活运用。
说到实际应用,这两个容器的用处可太广了。比如做一个简单的英文词典,key 存单词,value 存释义,用 map 再合适不过 ------ 既能快速查找,又能保证单词不重复;比如做一个学生成绩管理系统,key 存班级号,value 存学生信息,用 multimap 就能轻松实现 "一个班级对应多个学生" 的需求,还能按班级号自动排序;再比如处理配置文件,key 存配置项名称,value 存配置值,map 的有序性让配置遍历更清晰,[]运算符让配置修改更便捷。这些场景咱们平时写代码都可能遇到,现在掌握了 map 和 multimap,下次遇到类似问题就能直接 "拿来即用",不用再手动写排序和映射逻辑啦。
学习过程中,咱们肯定也遇到过一些 "小卡点" 吧?比如刚开始不知道怎么给 map 插入元素,忘了要传 pair 结构体;比如混淆了 map 和 multimap 的接口差异,不小心在 multimap 里用了[]导致编译报错;比如用迭代器访问 key 和 value 时,分不清该用->还是.。其实这些都是正常的,技术学习本就是 "遇到问题 --- 解决问题 --- 加深理解" 的过程。就像咱们在示例代码里写的 Test_Map_Construct、Test_Map_Modifiers 函数,把构造、增删、查询拆分成一个个小任务,一步步调试运行,很多难点自然就迎刃而解了。
这里还要特别夸夸坚持到现在的你~ 从二叉搜索树的基础,到 set、multiset 的 "单值有序容器",再到 map、multimap 的 "键值映射容器",咱们一步一个脚印,不仅掌握了具体的容器用法,更培养了 "底层逻辑决定上层接口" 的思维方式。比如知道了红黑树的有序性,就理解了为什么 map 和 multimap 遍历是有序的;知道了红黑树的平衡特性,就明白为什么它们的增删查效率都是 O (logN);知道了 map 的 key 唯一,就懂了为什么 insert 会返回 pair<bool>,而 multimap 不需要。这种 "知其然更知其所以然" 的学习方式,会让你在后续的技术路上走得更稳、更远。
可能有小伙伴会说:"我平时写代码好像用不上这么复杂的容器",但其实 map 和 multimap 的价值,远不止 "存储数据" 这么简单。它们代表的 "键值映射" 思维,是编程中解决关联数据问题的核心思路 ------ 比如前后端交互时的参数映射、数据库查询后的结果封装、业务逻辑中的配置管理,本质上都是 "键值对应" 的场景。今天咱们学好 map 和 multimap,不仅是掌握了两个 STL 容器,更是学会了一种高效解决关联数据问题的思维模式,这才是最宝贵的收获。
另外,咱们也要注意容器的 "选型智慧"。map 和 multimap 虽然好用,但不是万能的:如果不需要 key 有序,只是想要快速的键值映射,unordered_map 的哈希表实现能提供 O (1) 的平均效率;如果需要随机访问元素,vector 的连续内存会更有优势;如果不需要 value,只是要唯一有序的元素,set 比 map 更简洁。技术没有优劣之分,只有 "是否合适",咱们要做的就是根据实际场景,选择最贴合需求的工具 ------ 这也是成为优秀开发者的必经之路。
回顾咱们的 STL 学习之旅,从二叉搜索树的底层原理,到 set、multiset 的 "单值有序",再到 map、multimap 的 "键值映射",每一步都是循序渐进的积累。可能刚开始接触 pair 结构体、仿函数、迭代器的时候,你会觉得有点抽象,但只要像今天这样,把复杂知识点拆成一个个小函数(比如 Test_Map_Construct 负责构造、Test_Map_Modifiers 负责增删),动手敲代码、调试运行,慢慢就能找到感觉。就像咱们在示例代码里反复验证的:map 的 [] 运算符怎么用、multimap 的 equal_range 怎么遍历、pair 结构体怎么插入,这些实操经验远比死记硬背文档更有用。
最后,想对正在坚持的你说一句:技术学习从来没有 "一蹴而就",但每一次坚持都有意义。咱们一边啃 Linux 的硬核知识,一边补 C++ 的基础功底,看似双线并行,实则是在构建一个更完整的技术体系。今天你吃透了 map 和 multimap,明天再遇到 unordered_map、unordered_multimap 时,就能轻松上手;未来做项目遇到关联数据处理、配置管理、多值映射等问题时,也能毫不犹豫地拿出今天学到的知识,高效解决问题。
学习的路上,难免会遇到卡点 ------ 比如忘了 multimap 不支持 []、混淆了 lower_bound 和 upper_bound 的区别、不知道 pair 结构体怎么初始化。但没关系,这些都是学习的常态,重要的是保持好奇心和耐心,多回头看看示例代码,多动手实践几次,很多问题自然就迎刃而解了。而且你要相信,你现在付出的每一份努力,都在为未来的技术之路铺路,今天积累的每一个知识点,终会在某个时刻帮你解决大问题。
咱们常说 "工欲善其事,必先利其器",STL 容器就是 C++ 开发者手中的 "利器",而 map 和 multimap 这两个 "键值映射利器",更是让咱们处理关联数据时如虎添翼。接下来,咱们还会继续探索 STL 的其他宝藏容器,比如 unordered 系列的哈希容器、stack 和 queue 的栈队列结构、vector 和 list 的线性容器,一步步构建起完整的 STL 知识体系。
感谢大家耐下心来读完这篇博客,也感谢你在学习过程中始终保持的热情和坚持~ 希望这篇关于 map 和 multimap 的分享,能让你不仅掌握具体的用法,更能体会到 STL 设计的精妙和编程思维的魅力。未来的技术之路还很长,但只要咱们保持这份 "嘎嘎学习" 的劲头,一步步稳扎稳打,终会在自己擅长的领域闪闪发光。
最后,送给大家一句话:编程的本质是解决问题,而学习容器的本质是掌握更高效的解决工具。愿你带着今天学到的知识,在 C++ 的世界里继续探索、不断进阶,咱们下次博客再见!