在掌握unordered_map容器的基础下,我们开始unordered_multimap容器的学习。为什么要求有unordered_map容器的基础呢,原因在于unordered_multimap容器与unordered_map容器十分“相似”,不同点在于unordered_map容器只允许存在唯一键,而unordered_multimap容器能够存储重复键,在理解unordered_map容器的基础上对比归纳这两个容器,能够对关联式容器有个宏观的视角,提高对容器的掌控力。如果读者没有unordered_map容器的基础,可跳转《C++ STL unordered_map容器入门》开始unordered_map容器的学习。
由于unordered_multimap容器允许存在相同键,在对比unordered_map容器的学习下读者有什么感想?要知道,unordered_map容器由于键的唯一性,能够通过'[]'、at()访问元素,但是对于unordered_multimap容器来说,重复键值意味着一个键对应多个值,不能形成一一对应的关系,所以unordered_multimap容器没有'[]'和at()。其他成员函数与unordered_map容器几乎相差无几,值得一提的是,equal_range()在unordered_multimap容器里能够大放异彩。接下来,我们就开始认识unordered_multimap容器吧:
创建unordered_multimap容器与unordered_map容器没有差别,这里代码简单演示一下:
#include<bits/stdc++.h>//万能头,以效率为中心 #include<string> #include<unordered_map>//包含头文件,养成好习惯 /*multimap初始化 */ using namespace std; void test() { unordered_multimap<int,int> ummp1;//直接创建 unordered_multimap<int,int> ummp2{{1,1,},{2,2},{3,3},{4,4},{5,5}} ;//初始化列表 unordered_multimap<int,int> ummp3(ummp2) ;//拷贝构造 unordered_multimap<int,int> ummp4; ummp4=ummp2;//'='运算符重载 unordered_multimap<int,int> ummp5(ummp2.begin(),ummp2.end());//通过迭代器区间进行初始化 } int main(){ test(); return 0; }
代码编译成功,我们总共有5种方法创建和初始化unordered_multimap容器。
unordered_multimap容器的成员函数相比unordered_map没有at(),同时也未能重载'[]'所以不能使用'[]'。下面我们通过表格的形式展示unordered_multimap的常用成员函数:
函数 | 参数 | 功能 |
---|---|---|
empty() | 无 | 检查容器是否为空 |
size() | 无 | 返回元素数量 |
max_size() | 无 | 返回可容纳的最大元素数 |
begin() | 无 | 返回指向起始的迭代器 |
end() | 无 | 返回指向末尾的迭代器 |
cbegin() | 无 | 返回指向起始的常量迭代器 |
cend() | 无 | 返回指向末尾的常量迭代器 |
clear() | 无 | 清空所有元素 |
bucket_count() | 无 | 返回桶的数量 |
max_bucket_count() | 无 | 返回桶的最大数量 |
load_factor() | 无 | 返回负载因子 |
max_load_factor() | 无 | 返回最大负载因子 |
hash_function() | 无 | 返回哈希函数对象 |
key_eq() | 无 | 返回键相等性判断函数对象 |
insert() | const value_type& value | 插入元素 |
InputIt first, InputIt last | 插入迭代器范围内的元素 | |
initializer_list<value_type> ilist | 插入初始化列表中的元素 | |
emplace() | Args&&... args | 原位构造元素 |
emplace_hint() | const_iterator hint, Args&&... args | 在提示位置原位构造元素 |
erase() | const_iterator pos | 删除指定位置的元素 |
const key_type& key | 删除所有键为key的元素 | |
const_iterator first, const_iterator last | 删除迭代器范围内的元素 | |
find() | const key_type& key | 查找键为key的元素 |
count() | const key_type& key | 返回键为key的元素数量 |
equal_range() | const key_type& key | 返回键为key的元素范围 |
bucket_size() | size_type n | 返回第n个桶的大小 |
bucket() | const key_type& key | 返回键key所在的桶编号 |
rehash() | size_type count | 设置桶数为count并重新哈希 |
reserve() | size_type count | 预留空间至少容纳count个元素 |
对于unordered_multimap容器,它是没有lower_bound()和upper_bound(),但是有equal_range(key),通过它我们能够找到键==key的所有键值对。如果读者不理解无序关联式容器的底层构造,此时心里会想,unordered_multimap容器不是以“无序”著称吗,元素分散排列,它是如何通过equal_range(key)找到所有键==key的呢?有这个疑惑的读者可跳转《深度剖析C++ STL无序关联式容器的底层构造》寻求答案。下面我们通过代码来学习equal_range(key):
#include<bits/stdc++.h>//万能头,以效率为中心 #include<string> #include<unordered_map>//包含头文件,养成好习惯 /*unordered_multimap*/ using namespace std; /*equal_range()*/ void test() { unordered_multimap<int ,string> ummp{{9,"Dptcpp"},{1,"编程问答"},{9,"计算机二级C语言"},{4,"数据结构教程"},{9,"Java教程"}};//建议这样初始化 ,直观具体 /*equal_range()等于key的迭代器区间 */ cout << "equal_range()找到键==Key的区间: \n"; auto range = ummp.equal_range(9) ;//auto省略了:pair<unordered_multimap<string,string>::iterator,unordered_multimap<string,string>::iterator> for(auto it = range.first;it!=range.second;++it) //迭代器遍历 { cout << it->second <<'\n'; } } int main(){ test(); return 0; }
编译结果如下:
当然,学习unordered_multimap容器,我们还需要学习其关于哈希的成员函数的使用技巧。在已知键值对数量n的情况下,我们可以通过reserve()来预存比n稍大的空间;对于负载因子的变化,如果我们想节省空间,则尽量控制在0.8-1.0之间,如果我们着眼于访问效率,则尽量控制在0.5-0.75之间。
总结:unordered_multimap容器与unordered_map容器需要对比起来学习,操作unordered_multimap容器就不能使用'[]'和at(),而且要牢牢掌握equal_range()这个函数,读者需勤加练习,反复记忆和实践。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程