STL —— map用法及实例详解(超详细完整)
Posted 薛定谔的猫ovo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL —— map用法及实例详解(超详细完整)相关的知识,希望对你有一定的参考价值。
文章目录
map容器的基本性质
作为关联式容器的一种,map容器存储的都是pair对象,也就是用pair类模板创建的键值对。其中各个键值对的键和值可以是任意数据类型,包括基本输出类型、结构体或自定义的类型。通常情况下,map容器中存储的各个键值对一般使用string字符串作为键的类型。
在使用map容器存储多个键值对时,该容器会根据各个键值对的键的大小,按照既定的规则进行排序。
默认情况下,map容器选用std::less<T>排序规则(其中T表示键的数据类型),会根据键的大小对所有键值做升序排序。
当然,根据实际形况需要,也可以手动指定map容器的排序规则,选用STL标准库中提供的其他排序规则(例如std::greater<T>),也可以自定义排序规则。
另外,使用map容器存储的各个键值对时,键的值既不能重复也不能被修改。也就是说,map容器中存储的各个键值对不仅键的值独一无二,键的类型也会用const修饰,意味着只要键值对被存储到map容器中,其键的值将不能在做任何修改。
前面提到map容器存储的都是pair类型的键值对元素,更确切的说是pair<const K, T>类型(其中K和T分别表示键和值的数据类型)的键值对元素。
STL —— map的用法
map容器定义在<map>头文件中,并位于std命名空间中,如果想要使用map容器,应该引入头文件:
#include<map>
map容器的模板定义如下:
template < class Key, // 指定键(key)的类型
class T, // 指定值(value)的类型
class Compare = less<Key>, // 指定排序规则
class Alloc = allocator<pair<const Key,T> > // 指定分配器对象的类型
> class map;
其中后2个参数都设有默认值,大多数时候只需设置前2个参数的值,有时候会用到第3个参数,最后一个参数几乎不会用到。
创建map容器
map容器的模板类中包含多种构造函数,因此创建map容器的方式也有多种。
1)调用map容器类的默认构造函数map(),创建一个空的map容器
map<K,V> myMap; //创建一个名为myMap的空map对象,其键和值的类型分别为K和V,例如:
map<string, int> myMap;
通过这个方式创建的map容器,初始状态下是空的,即没有存储任何键值对。
2)当然也可以在创建map容器的同时进行初始化
map<string, int> myMap{{"penny",1},{"leonard",2}};
经过上述操作,myMap在初始状态下,就包含2个键值对。
当然,map容器中存储的键值对,其本质都是pair类模板创建的pair对象。因此,下面的代码也可以创建出一样的myMap容器:
map<string, int> myMap{make_pair("penny", 1),make_pair("leonard", 2)};
3)拷贝(复制)构造函数
map<K, V> myMap1(myMap); //利用myMap创建一个名为myMap1的新容器,其键和值的元素类型均相同。
>map<string, int> myMap{{"penny",1},{"leonard",2}};
>map<string, int> myMap1(myMap);
4)获取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器。
map<string, int> myMap{make_pair("penny", 1),make_pair("leonard", 2)};
map<string, int> myMap1(++myMap.begin(), myMap.end());
执行完上述代码后,myMap1容器中包含的键值对为{“penny”,1}。
在以上几种创建map容器的基础上,也可以手动修改map容器的排序规则。
默认情况下,map容器调用std::less<T>规则,根据容器内各键值对的键的大小,对所有的键值进行升序排序。
因此,下面两行代码是等价的:
map<string, int> myMap{{"penny",1},{"leonard",2}};
map<string, int, std::less<std::string>> myMap{{"penny",1},{"leonard",2}};
//若程序中已经默认指定了std命名空间,故也可以省略std::
若想要元素降序排序,则:
map<string, int, std::greater<std::string>> myMap{{"leonard",2},{"penny",1}};
执行完该语句,myMap中的键值对排列顺序为:{“penny”,1},{“leonard”,2}
容器的大小
map容器中提供了count()、max_size()、size()分别用于统计元素的个数、求容器的最大存储容量和容器大小。
此外,还可以用empty()判断当前map容器是否为空。
1)count(key),统计键的值为key的元素的个数,由于map中没有重复的元素,因此其计算结果只有0和1,而multimap会有多个值。
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3},{"howard",4}};
int num = myMap.count("penny");
cout<<num<<endl; //1
2)max_size()用于求容器最大存储量。通常与机器本身的限制有关。
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3},{"howard",4}};
cout<<myMap.max_size()<<endl; //256204778801521550
3)size()用于统计当前容器的大小,也就是容器中键值对的个数。
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3},{"howard",4}};
cout<<myMap.size()<<endl; //4
4)empty()用于判断当前map容器是否为空。
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3},{"howard",4}};
cout<<myMap.empty()<<endl; //0
map容器中键值对的访问与遍历
无论是序列式容器还是关联式容器,要想实现遍历操作,就必须要用到该类型容器的迭代器。
C++ STL标准库为map容器配备的是双向迭代器,这意味着map容器迭代器只能行++p、p++、–p、p–、*p操作,并且迭代器之间只能使用==或者!=运算符进行比较。
map容器迭代器中成员方法:
成员方法 | 功能 |
---|---|
begin() | 返回指向容器中第一个(已排好序的第一个)键值对的双向迭代器。 |
end() | 返回指向容器中最后一个元素(已排好序的最后一个)所在位置的后一个位置的双向迭代器。 |
rbegin() | 返回指向容器中最后一个(已排好序的最后一个)元素的反向双向迭代器。 |
rend() | 返回指向容器中第一个(已排好序的第一个)元素所在位置的前一个位置的反向双向迭代器。 |
cbegin() | 和begin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
cend() | 和end()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
crbegin() | 和rbegin()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
crend() | 和rend()功能相同,只不过在其基础上,增加了const属性,不能用于修改容器内储存的键值对。 |
find(key) | 在map容器中查找键为key的键值对,若成功找到,则返回指向该键值对的双向迭代器;若未找到,则返回和end()方法一样的迭代器。 |
lower_bound(key) | 返回一个指向当前map容器中第一个大于或等于key的键值对的双向迭代器。 |
upper_bound(key) | 返回一个指向当前map容器中第一个大于key的键值对的双向迭代器。 |
equal_range(key) | 返回一个pair对象(包含2个双向迭代器),其中pair.first和lower_bound()方法的返回值等价,pair.second和upper_bound()方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为key的键值对(map容器键值对唯一,因此该返回最多包含一个键值对)。 |
关于begin() 、end()、rbegin()、rend()函数的功能示意图:
图中Ei表示的是pair类对象,即键值对。
遍历map容器
以begin()/end()组合为例,遍历map容器:
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string, int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
for(auto iter = myMap.begin(); iter != myMap.end(); iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
执行结果:
也可以用其他组合,例如cbegin()/cend()、rbegin()/rend()等遍历map容器。
find(key)查找指定key值的键值对
find()方法可以查找指定key值的键值对,如果成功找到则返回一个指向该键值对的双向迭代器;反之,其功能和end()方法相同。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
auto iter = myMap.find("sheldon");
//从iter开始,遍历map容器
for(; iter != myMap.end(); iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
执行结果:由上一个遍历可知,要查找的键值对排序在最后一个,故从最后一个开始遍历map会得到以下结果:
lower_bound(key)与upper_bound(key)
lower_bound(key)返回的是指向第一个键大于或等于(不小于)key的键值对的迭代器。
upper_bound(key)返回的是指向第一个键大于key的键值对的迭代器。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
//找到第一个键的值不小于"penny"的键值对
auto iter = myMap.lower_bound("penny");
cout<<iter->first<<" "<<iter->second<<endl;
//找到第一个键的值大于"penny"的键值对
iter = myMap.upper_bound("penny");
cout<<iter->first<<" "<<iter->second<<endl;
return 0;
}
执行结果:
equal_range(key)
equal_range(key)方法可以看作是lower_bound(key)和upper_bound(key)的结合体,该方法会返回一个pair对象,其中的2个元素都是迭代器类型,其中pair.first实际上就是lower_bound(key)的返回值,而pair.second则等同于upper_bound(key)的返回值。
显然,equal_range(key)方法表示的一个范围,位于此范围中的键值对,其键的值都为key,而map中的键都是唯一的,故返回的范围内最多只有一个键值对。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
//创建一个pair对象,来接收equal_range()的返回值
pair<map<string,int>::iterator, map<string,int>::iterator> myPair;
myPair = myMap.equal_range("leonard");
for(auto iter = myPair.first; iter!=myPair.second; iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
执行结果:
map获取键对应的值
map容器的类模板中提供了以下2种方法,可以直接获取容器指定键对应的值。
[ ]运算符
1)map类模板中对[ ]运算符进行了重载,类似于借助数组下标可以直接访问数组中的元素,通过指定的键,可以获取该map容器中该键对应的值。例如:
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
int value = myMap["penny"];
cout<<value<<endl; //1
return 0;
}
注意,只有当map容器中包含该指定键的键值对时,借助重载的[ ]运算符才能成功获取该键对应的值;否则,若当前map容器中没有包含该指定键的键值对,则此时使用[ ]运算符将不再是访问容器中的元素,而变成了向该map容器中增添一个键值对。
其中,该键值对的键用[ ]运算符中指定的键,其对应的值取决于map容器中规定键值对中值的数据类型,如果是基本数据类型,则值为0;如果是string类型,其值为" ",即空字符串,也就是说使用该类型的默认值作为键值对的值。
at()成员方法
2)at()成员方法也可以获取指定键对应的值。和通过[ ]运算符获取方法不同的是:如果在当前容器查找失败,该方法不会向容器中添加新的键值对,而是直接抛出out_of_range异常。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
int value = myMap.at("penny");
cout<<value<<endl;
value = myMap.at("howard");
cout<<value<<endl;
return 0;
}
执行结果为:
find()成员方法
3)此外,还可以使用上面的find()成员方法间接实现此目的。
和上面两种方法不同的是,该方法返回一个迭代器,如果查找成功,该迭代器指向查找到的键值对;反之,则指向map容器中最后一个键值对之后的位置,即和end()成功方法返回的迭代器一样。
4)最后,也可以采用遍历整个map容器的方法,找到包含指定键的键值对,进而获取该键对应的值。
map插入数据
[ ]运算符
1)前面提到过一次,C++ STL类模板中对[ ]运算符进行了重载,即根据使用场景的不同,可以实现不同的操作。可以使用[ ]运算符类似数组添加元素那样添加键值对。
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
map<string,int> myMap{{"penny",1},{"leonard",2},{"sheldon",3}};
myMap["howard"] = 4;
for(auto iter = myMap.begin(); iter != myMap.end(); iter++){
cout<<iter->first<<" "<<iter->second<<endl;
}
return 0;
}
执行结果:
insert()成员方法
2)map类模板中提供了insert()成员方法,该方法专门用来向map容器中插入新的键值对。
注意:这里的“插入”指的是insert()方法可以将新的键值对插入到map容器中的指定位置,但这与map容器会自动对存储的键值对进行排序并不冲突。当使用insert()方法向map容器的指定位置插入新键值对时,其底层会先将新键值对插入到容器的指定位置,如果其破坏了map容器的有序性,该容器会对新键值的位置进行调整。
从C++ 11标准后,insert()成员方法的用法大致有以下4种。
2.1)无需指定插入位置,直接将键值对添加到map容器中。
//1.引用传递一个键值对
pair<iterator,bool> insert (const value_type& val);
//2.以右值引用的方式传递键值对
tempalte<class P>
pair<iterator,bool> insert(P&&val);
val参数表示键值对变量。
该方法返回一个pair对象,其中pair.first表示一个迭代器,pair.second为一个bool类型变量。
- 如果成功插入val,则该迭代器指向新插入的val,bool的值为true。
- 如果插入val失败,则表明当前map容器种存在和val的键相同的键值对p,此时返回的迭代器指向p,bool的值为false。
以上两种语法的区别在于传递参数的方式不同,无论是局部定义还是全局定义的键值对变量,都采用普通引用传递的方式;而对于临时的键值对变量,则以右值引用的方式传参。
具体实例:
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main(){
//创建一个空map容器
map<string,int> myMap;
//创建一个键值对变量
pair<string,int> myPair = {"penny", 1};
//创建一个接收Insert()方法返回值的pair对象
pair<map<string,int>::iterator, bool> ret;
//1.插入myPair,由于myPair并不是临时变量,因此会用第一种方式传参
ret = myMap.insert(myPair);
cout<<ret.first->first<<" "<<ret.first->second<<", "<<ret.second<<endl;
//2.以右值引用的方式传递临时的键值对变量
ret = myMap.insert({"sheldon",2});
cout<<ret.first->first<<" "<<ret.first->second<<", "<<ret.second<<endl;
//插入失败的情况
ret = myMap.insert({"penny",2});
cout<<ret.first->first<<" "<<ret.first->second<<", "<<ret.second<<endl;
return 0;
}
执行结果:
总共执行了3次插入操作,成功2次,失败了1次。
2.2)insert()方法向map容器的指定位置插入新的键值对。
//1.普通引用的方式传递val参数
iterator insert(const_iterator position以上是关于STL —— map用法及实例详解(超详细完整)的主要内容,如果未能解决你的问题,请参考以下文章