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用法及实例详解(超详细完整)的主要内容,如果未能解决你的问题,请参考以下文章

STL中的map用法详解

map用法详解

c语言指针用法及实际应用详解,通俗易懂超详细!

Python urlopen() & urlretrieve() 用法解析及实例演示(超详细)

C++中STL用法超详细总结(收藏级)

C++中的STL中map用法详解