C++ STL map容器

等待樱花盛开

一、前言

在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,会根据键的大小对所有键值对做升序排序。
注意点:使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改,即键的类型会用 const 修饰,且是一对一的关系。
使用map容器应包含头文件:

#include <map>
using namespace std; //不是必需的

1、map容器的模板定义

template < class Key,  // 指定键(key)的类型                                
           class T,    // 指定值(value)的类型                               
           class Compare = less<Key>,  // 指定排序规则                   
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型
           > class map;

虽然map的模板有4个参数,但是大多数情况下只需要设置前2个参数即可。

二、创建map容器方法

①std::map<std::string, std::string> myMap;
②std::map<std::string, std::string> myMap{ {"1","lin"},{"2","wei"} };
③std::map<std::string, std::string> newMap(myMap);
④std::map<std::string, std::string> newMap(myMap.begin(), myMap.end());
⑤std::map<std::string, std::string, std::less<std::string> > myMap{ {"1","lin"},{"2","wei"} };

三、map容器方法

此处不列举迭代器、插入和删除相关方法

方法功能
find(key)在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
lower_bound(key)返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
upper_bound(key)返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
equal_range(key)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)
empty()若容器为空,则返回 true;否则 false
size()返回当前 map 容器中存有键值对的个数
max_size()返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同
operator[]map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值
at(key)找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常
count(key)在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1

1、实例

1)、lower_bound、upper_bound和equal_range

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
std::map<int, string>::iterator mapIter = mapInfo.lower_bound(1);
std::cout << "lower_bound key: " << mapIter->first << " value: " << mapIter->second << std::endl;

mapIter = mapInfo.upper_bound(1);
std::cout << "upper_bound key: " << mapIter->first << " value: " << mapIter->second << std::endl;

pair<map<int, string>::iterator, map<int, string>::iterator> iters = mapInfo.equal_range(3);
std::map<int, string>::iterator beginIter, endIter;
beginIter = iters.first;
endIter = iters.second;
for (beginIter; beginIter != endIter; beginIter++)
{
    std::cout << "equal_range key: " << beginIter->first << " value: " << beginIter->second << std::endl;
}

结果如下:
image.png

三、迭代器

stl标准库为map容器配备的是双向迭代器(bidirectional iterator),因此map 容器迭代器只能进行 ++p、p++、--p、p--、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。

方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。

这个的具体功能不再说明,和序列化容器是一样的功能,唯一不同的就是这里的数据是“键值对”。
对于迭代器的使用,已经有很多的实例,这里就不再说明了。

四、map获取键对应的值

1、通过[]获取

类似于访问数组元素的方式。

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
string strData = mapInfo[1];  
std::cout << "strData: " << strData << std::endl;

结果如下:
image.png
注意:
1)只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值
2)若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
mapInfo[5] = "li";
std::map<int, string>::iterator mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << "key: " << mapIter->first << " value: " << mapIter->second << std::endl;
}

结果如下:
image.png

3)其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 "",即空字符串(即使用该类型的默认值作为键值对的值)
4)[]具有添加新的键值对的前提是:当前map容器中不存在新添加的键
5)如map容器中已存在对应键,执行map[key] = value是修改键key的值,而不是为 map 容器添加新键值对

mapInfo[5] = "wne";
mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << "mod key: " << mapIter->first << " value: " << mapIter->second << std::endl;
}

结果如下:
image.png

2、at()

需要指定key,才能从容器中找到该键对应的值;如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
mapInfo[5] = "li";

try
{
    std::cout << mapInfo.at(3) << std::endl;
    std::cout << mapInfo.at(10) << std::endl;
       
}
catch (const std::exception&)
{
    std::cout << "调用at获取键对应的值异常" << std::endl;
}

结果如下:
image.png

3、find

如果查找成功,该迭代器指向查找到的键值对;反之,则指向 map 容器最后一个键值对之后的位置(和 end() 成功方法返回的迭代器一样)。

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
mapInfo[5] = "li";

std::map<int, string>::iterator mapIter = mapInfo.find(5);
if (mapIter != mapInfo.end())
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}

结果如下:
image.png

4、若以上方法都不能获取到值,那只能遍历整个map容器查找,进而获取值
具体代码在这里就不写了

五、插入数据

前面已经说过,可以通过[]运算符修改或者添加键值对,在这里就不说这种用法了。

1、insert

insert方法是专门用来向 map 容器中插入新的键值对的。这里的"插入"指的是 insert() 方法可以将新的键值对插入到 map 容器中的指定位置。如果破坏了map容器的有序性,map容器会对新键值对的位置进行调整,也就是说,虽然insert可以将键值对插入指定的位置,但是插入之后map容器会检查插入的键值对是否符合有序性,不符合的话insert指定的位置就不是插入键值对真正的位置了。

1)不指定位置,直接插入

格式说明
pair<iterator,bool> insert (const value_type& val);引用传递一个键值对
template <class P> pair<iterator,bool> insert (P&& val);以右值引用的方式传递键值对

区别:传递参数的方式不同。无论是局部定义的键值对变量还是全局定义的键值对变量,都采用普通引用传递的方式;而对于临时的键值对变量,则以右值引用的方式传参。

std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };

//格式1
std::pair<int, string> mapData = { 4, "wu" };
std::pair<std::map<int, string>::iterator, bool> ret;
ret = mapInfo.insert(mapData);
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;

//格式2
ret = mapInfo.insert({ 5,"ouyang" });
//等价于
//ret = mapInfo.insert(pair<int, string>{5, "ouyang"});
//ret = mapInfo.insert(make_pair(5, "ouyang"));
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;

//插入失败
ret = mapInfo.insert({ 1,"lu" });
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;
    
std::map<int, string>::iterator mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}
   

结果如下:
image.png
由结果可知:
①返回值是一个 pair 对象,其中 pair.first 表示一个迭代器,pair.second 为一个 bool 类型变量:
如果成功插入 val,则该迭代器指向新插入的 val,bool 值为 true;
如果插入 val 失败,则表明当前 map 容器中存有和 val 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool 值为 false。

2)指定位置插入

格式说明
iterator insert (const_iterator position, const value_type& val);以普通引用的方式传递 val 参数
template <class P> iterator insert (const_iterator position, P&& val);以右值引用的方式传递 val 键值对参数
std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };

//格式1
std::pair<int, string> mapData = { 4, "wu" };
std::map<int, string>::iterator mapIter = mapInfo.begin();
std::map<int, string>::iterator insertIter = mapInfo.insert(++mapIter, mapData);
std::cout << insertIter->first << "  " << insertIter->second << std::endl;

//格式2
insertIter = mapInfo.insert(++mapIter, std::pair<int, string>(5, "kai"));
std::cout << insertIter->first << "  " << insertIter->second << std::endl;

//插入失败
insertIter = mapInfo.insert(++mapIter, std::pair<int,string>(3,"ouyang"));
std::cout << insertIter->first << "  " << insertIter->second << std::endl;

mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}

结果如下:
image.png
由结果可知:
①和不指定位置的插入的方法的区别,这里返回的是迭代器
如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。

3)向当前 map 容器中插入其它 map 容器指定区域内的所有键值对

格式
template <class InputIterator> void insert (InputIterator first, InputIterator last);
std::map<int, string> mapInfo{ {1,"test"},{2,"lin"},{3,"wei"} };
std::map<int, string> mapInfo2;

std::map<int, string>::iterator first = ++mapInfo.begin();
std::map<int, string>::iterator last = mapInfo.end();
   
mapInfo2.insert(first, last);

std::map<int, string>::iterator mapIter = mapInfo2.begin();
for (; mapIter != mapInfo2.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}
   

结果如下:
image.png
由结果可知:
①需要其他map容器的开始和结束的迭代器,组成一个指定区域

4)一次向 map 容器中插入多个键值对

表列 A
void insert ({val1, val2, ...});
std::map<int, string> mapInfo;
mapInfo.insert({ {1,"test"},{2,"lin"},{3,"wei"} });

std::map<int, string>::iterator mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}

结果如下:
image.png

insert的介绍就到这里,接下来讲讲比insert高效的方法

2、emplace

格式
template <class... Args> pair<iterator,bool> emplace (Args&&... args);
std::map<int, string> mapInfo;
pair<map<int, string>::iterator, bool> ret = mapInfo.emplace(1, "test");
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;
    
ret = mapInfo.emplace(2, "lin");
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;

ret = mapInfo.emplace(2, "kai");
std::cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << std::endl;
std::map<int, string>::iterator mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}

结果如下:
image.png
由结果可知:
①使用emplace方法时,将创建新键值对所需的数据作为参数直接传入即可,返回值是一个pair对象,和不指定位置的insert方法的返回值一样

3、emplace_hint

格式
template <class... Args> iterator emplace_hint (const_iterator position, Args&&... args);

该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数,指明要插入的位置。

std::map<int, string> mapInfo;
map<int, string>::iterator iter = mapInfo.emplace_hint(mapInfo.begin(), 1, "test");
std::cout << iter->first << "  " << iter->second << std::endl;

iter = mapInfo.emplace_hint(mapInfo.begin(), 2, "lin");
cout << iter->first << " " << iter->second << endl;

iter = mapInfo.emplace_hint(mapInfo.begin(), 2, "kai");
cout << "insert 2 kai  finial " << iter->first << " " << iter->second << endl;
std::map<int, string>::iterator mapIter = mapInfo.begin();
for (; mapIter != mapInfo.end(); mapIter++)
{
    std::cout << mapIter->first << "  " << mapIter->second << std::endl;
}

结果如下:
image.png
①返回值是一个迭代器。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明 map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对

阅读 987

小菜鸟一枚,望各位大佬不吝指教,用于记录学习

1 声望
0 粉丝
0 条评论

小菜鸟一枚,望各位大佬不吝指教,用于记录学习

1 声望
0 粉丝
文章目录
宣传栏