[STL 9] Associative container - map container, multimap container
1. Introduction to map
- The map container stores all pair objects, that is, key-value pairs created with the pair class template.
- Both key and value must be copyable (copyable), movable (movable);
- When using the map container to store multiple key-value pairs, the container will automatically sort according to the established rules according to the size of the key of each key-value pair.
By default, the map container uses the std::less sorting rule (where T represents the data type of the key), which sorts all key-value pairs in ascending order according to the size of the key. Of course, according to the needs of the actual situation, we can manually specify the sorting rules of the map container, or choose other sorting rules provided in the STL standard library (such as std::greater)
- Keys must be comparable for the specified ordering criteria.
Use each key-value pair stored in the map container, and the value of the key can neither be repeated nor modified
Two, the header file
#include<map>
3. Template class
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>
> class map;
Fourth, the internal structure of the map
- map/multimap is usually done as a balanced binary tree; (same as other associative containers)
- Map and multimap will automatically sort the elements according to the key of the element.
- In this way, it can be very efficient when searching for an element based on a known key.
- When looking up elements based on known values, the efficiency is very bad.
- The nature of automatic sorting makes maps and multimaps have an important limitation: you cannot directly change the key of the element. (Because this will destroy the correct order)
To modify the key of an element, the element with the key must be removed first, and then the element with the new key/value must be inserted. (If the value is not constant, it can be modified)
5. Member functions
1. Iterator
member function | Function |
---|---|
begin() | Same array container |
end() | Same array container |
rbegin() | Same array container |
rend() | Same array container |
cbegin() | Same array container |
a few() | Same array container |
crbegin() | Same array container |
crend() | Same array container |
2. Element access
member function | Function |
---|---|
operator[] | Same array container |
at(n) | Same array container |
3. Capacity
member function | Function |
---|---|
size() | Same array container |
max_size() | Same array container |
empty() | Same array container |
4. Modify operation
member function | Function |
---|---|
clear() | Same as vector container |
insert() | Same as vector container |
insert_or_assign(C++17) | inserts the element, or assigns to the current element if the key already exists |
place() | Same as vector container |
emplace_hint() | It is essentially the same as how emplace() constructs a new key-value pair in a map container. The difference is that the user must provide this method with an iterator indicating the location where the key-value pair is generated, and use it as the method's first parameter. |
try_emplace(C++17) | Insert in place if the key does not exist, do nothing if the key exists |
erase() | Same as vector container |
swap() | Same as vector container |
extract(C++17) | release a node from another container |
merge(C++17) | join node from another container |
5. Operation
5. Find
member function | Function |
---|---|
count(key) | In the current map container, find the number of key-value pairs whose key is key and return it. Note that since the key value of each key-value pair in the map container is unique, the maximum return value of this function is 1. |
find(key) | Look up the key-value pair whose key is key in the map container. If found successfully, return a bidirectional iterator pointing to the key-value pair; otherwise, return the same iterator as the end() method. In addition, if the map container is const-qualified, this method returns a bidirectional iterator of const type. |
contains (C++20) | Checks if a container contains an element with a specific key |
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
Little a lives in Beijing
Little b lives in Xi’an
Little c lives in Puyang
Little d has no home
Little d has no home either
References:
1. C++ STL Container Library Chinese Documentation
2. STL Tutorial: C++ STL Quick Start
3. https://www.apiref.com/cpp-zh/cpp/header.html
4. https://en.cppreference. com/w/cpp/container