【STL九】关联容器——map容器、multimap容器
- 一、map简介
- 二、头文件
- 三、模板类
- 四、map的内部结构
- 五、成员函数
- 1、迭代器
- 2、元素访问
- 3、容量
- 4、修改操作
- ~~5、操作~~
- 5、查找
- 6、查看操作
- 六、demo
- 1、查找find
- 2、查找lower_bound、upper_bound
- 3、insert、emplace() 和 emplace_hint()
- 七、multimap
一、map简介
- map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。
- key和value都必须是可复制的(copyable),可移动的(movable);
- 在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。
默认情况下,map 容器选用std::less排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用 STL 标准库中提供的其它排序规则(比如std::greater)
- 对于指定的排序准则而言,key必须是可比较的(comparable)。
使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改
二、头文件
#include<map>
三、模板类
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>
> class map;
四、map的内部结构
- map/multimap通常以平衡二叉树完成;(同其他关联容器)
- map和multimap会根据元素的key自动对元素排序。
- 这样一来,根据已知的key查找某个元素时就能够有很好的效率,
- 而根据已知的value查找元素时,效率就很糟糕。
- 自动排序这一性质使得map和multimap身上有一个很重要的限制:你不可以直接改变元素的key.(因为这会破坏正确次序)
要修改元素的key,必须先移除拥有该key的元素,然后插入拥有新key/value的元素。(如果value非常量,就可以修改)
五、成员函数
1、迭代器
成员函数 | 功能 |
---|---|
begin() | 同array容器 |
end() | 同array容器 |
rbegin() | 同array容器 |
rend() | 同array容器 |
cbegin() | 同array容器 |
cend() | 同array容器 |
crbegin() | 同array容器 |
crend() | 同array容器 |
2、元素访问
成员函数 | 功能 |
---|---|
operator[] | 同array容器 |
at(n) | 同array容器 |
3、容量
成员函数 | 功能 |
---|---|
size() | 同array容器 |
max_size() | 同array容器 |
empty() | 同array容器 |
4、修改操作
成员函数 | 功能 |
---|---|
clear() | 同vector容器 |
insert() | 同vector容器 |
insert_or_assign(C++17) | 插入元素,或若键已存在则赋值给当前元素 |
emplace() | 同vector容器 |
emplace_hint() | 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
try_emplace(C++17) | 若键不存在则原位插入,若键存在则不做任何事 |
erase() | 同vector容器 |
swap() | 同vector容器 |
extract(C++17) | 从另一容器释出结点 |
merge(C++17) | 从另一容器接合结点 |
5、操作
5、查找
成员函数 | 功能 |
---|---|
count(key) | 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 |
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
contains (C++20) | 检查容器是否含有带特定键的元素 |
equal_range(key) | 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 |
lower_bound(key) | 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) | 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
6、查看操作
成员函数 | 功能 |
---|---|
key_comp | 返回用于比较键的函数 |
value_comp | 返回用于在value_type类型的对象中比较键的函数。 |
六、demo
1、查找find
- 返回值
指向键等于 key 的元素的迭代器。若找不到这种元素,则返回尾后(见 end() )迭代器。
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
cout << "i can find 小c:" << endl;
auto ite = mymap.find("小c");
cout << ite->first << "=" << ite->second << endl << endl;
auto ite2 = mymap.find("c");
if (ite2 == mymap.end())
{
cout << "i can not find c!" << endl;
// cout << ite->first << "=" << ite->second << endl << endl; //找不到时输出会报错的
}
return 0;
}
输出
i can find 小c:
小c=家在濮阳
i can not find c!
2、查找lower_bound、upper_bound
返回值
- lower_bound:指向首个不小于 key 的元素的迭代器。若找不到这种元素,则返回尾后迭代器
- upper_bound:指向首个大于 key 的元素的迭代器。若找不到这种元素,则返回尾后迭代器
正常场景
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
//找到第一个键的值不小于 "小c" 的键值对
auto iter = mymap.lower_bound("小c");
cout << "lower:" << iter->first << " " << iter->second << endl;
//找到第一个键的值大于 "小c 的键值对
iter = mymap.upper_bound("小c");
cout << "upper:" << iter->first << " " << iter->second << endl;
return 0;
}
输出
lower:小c 家在濮阳
upper:小d 没有家
临界场景
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
//找到第一个键的值不小于 "小c" 的键值对
auto iter = mymap.lower_bound("小d");
cout << "lower:" << iter->first << " " << iter->second << endl;
//找到第一个键的值大于 "小c 的键值对
iter = mymap.upper_bound("小d");
if (iter == mymap.end())
{
cout << "i can not find uper load 小d!" << endl;
}
return 0;
}
输出
lower:小d 没有家
i can not find uper load 小d!
3、insert、emplace() 和 emplace_hint()
- insert
insert() 方法返回的是迭代器,而不再是 pair 对象:- 如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;
- 如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
auto ret = mymap.insert({ "小e","家在台湾"});
cout << "insert:" << ret.first->first << " " << ret.first->second << "=" << ret.second << endl;
ret = mymap.insert({ "小e","家在台湾" });
cout << "insert:" << ret.first->first << " " << ret.first->second << "=" << ret.second << endl;
return 0;
}
输出
insert:小e 家在台湾=1
insert:小e 家在台湾=0
- emplace
emplace() 和 emplace_hint() 是 C++ 11 标准加入到 set 类模板中的,相比具有同样功能的 insert() 方法,完成同样的任务,emplace() 和 emplace_hint() 的效率会更高。
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
auto ret = mymap.emplace( "小e","家在台湾");
cout << "emplace:" << ret.first->first << " " << ret.first->second << "=" << ret.second << endl;
ret = mymap.emplace( "小e","家在台湾" );
cout << "emplace:" << ret.first->first << " " << ret.first->second << "=" << ret.second << endl;
return 0;
}
输出
emplace:小e 家在台湾=1
emplace:小e 家在台湾=0
- emplace_hint
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
map <string, string> mymap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
};
auto iter = mymap.emplace_hint(mymap.begin(), "小e","家在台湾");
cout << "emplace_hint:" << iter->first << " " << iter->second << endl;
iter = mymap.emplace_hint(mymap.begin(), "小e", "家在台湾");
cout << "emplace_hint:" << iter->first << " " << iter->second << endl;
return 0;
}
输出
emplace_hint:小e 家在台湾
emplace_hint:小e 家在台湾
虽然 emplace_hint() 方法指定了插入键值对的位置,但 map 容器为了保持存储键值对的有序状态,可能会移动其位置。(如下图)
七、multimap
map 容器的区别在于,multimap 容器中可以同时存储多(≥2)个键相同的键值对。
由于 multimap 容器可存储多个具有相同键的键值对,因此表 1 中的 lower_bound()、upper_bound()、equal_range() 以及 count() 成员方法会经常用到。
- 模板类
multimap 容器类模板的定义如下:
template < class Key, // 指定键(key)的类型
class T, // 指定值(value)的类型
class Compare = less<Key>, // 指定排序规则
class Alloc = allocator<pair<const Key,T> > // 指定分配器对象的类型
> class multimap;
- demo
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class multimap;
- demo
#include <iostream>
#include <string> // string
#include<map>
using namespace std;
int main() {
// 调用构造函数 1,也就是默认构造函数
multimap <string, string> mymultimap{
{"小b","家在西安"},
{"小c","家在濮阳"},
{"小a","家在北京"},
{"小d","没有家"},
{"小d","也没有家"},
};
//输出 mymultimap 容器中存储键为 'b' 的键值对的数量
cout << mymultimap.count("小d") << endl;
for (auto iter = mymultimap.begin(); iter != mymultimap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}
2
小a 家在北京
小b 家在西安
小c 家在濮阳
小d 没有家
小d 也没有家
参考:
1、C++ STL 容器库 中文文档
2、STL教程:C++ STL快速入门
3、https://www.apiref.com/cpp-zh/cpp/header.html
4、https://en.cppreference.com/w/cpp/container