stl--std::map
map
模板类
实现原理
红黑树存储键值对,红黑树的key就是传递的key,value是pair对象。
注意一些函数的参数,比如find函数:利用key寻找值,返回迭代器,而不是利用值寻找值。
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
map容器的迭代器
一定要注意:map容器中存放的不是键值对,也不是值。而是pair模板结构体,pair中才是存储键值对。
对map容器的迭代器进行指向操作,指向的是pair结构体中的函数或者变量。
对于map的迭代器(iterator),对其解引用后,就是一个pair对象,所以iterator->first = 键、iterator->second = 值。
map容器和其他容器迭代器解引用的相同点和不同点
相同点:解引用的结果都是容器中存储的元素(对象)。
不同点:
其他容器解引用出来的元素就是存储的值;
map容器解引用出来的是也是其元素,但是这个元素是一个pair对象,而不直接就是存储的值。
总结一句话:
容器中存储的是什么,迭代器解引用就是的结果就是什么。
map容器建立和存储的过程
建立map容器
容器内建立pair对象存储键值对
容器内建立RB-tree对象存储pair。
map容器初始化和赋值方法
1,赋值:{{},{}}赋值法。
2,拷贝:利用赋值构造函数:map<int,string>ffmap(fmap);
3,截取:
map 类模板还支持取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器。例如:
- std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} };
- std::map<std::string, int>newMap(++myMap.begin(), myMap.end());
这种方法注意end()的选择,如果要选择到最后,不要--mymap.end();
4,下标赋值。
map<int,string>mymap{{0,"fangfang"},{1,"xiaoli"}};auto ite=mymap.find(1);cout<<(*ite).second<<endl;cout<<ite->first<<" "<<ite->second<<endl;map<int,string> fmap;fmap[0]="aaaa";fmap[1]="bbbb";fmap[2]="cccc";int i=0;for(i;i<fmap.size();i++){cout<<fmap[i]<<" ";}cout<<endl;map<int,string>ffmap(fmap);for(i=0;i<ffmap.size();i++){cout<<ffmap[i]<<" ";}cout<<endl;
map容器不能像vector一样从数组中获取值。
5,insert插入法:
insert可以先建立pair键值对再插入,也可以插入临时值(右值引用插入):
int main()
{map<string,string>mymap;pair<map<string,string>::iterator,bool> ret=mymap.insert({"China","Beijing"});cout<<ret.first->first<<"---"<<ret.first->second<<endl;pair<string,string> add={"Americ","Niuyake"};ret=mymap.insert(add);cout<<ret.first->first<<"---"<<ret.first->second<<endl;return 0;
}
~
注意:
(1,pair键值对的定义,
(2,insert返回类型以及如何定义返回类型变量。
(3,insert还可以同时插入多个键值对。
关于Insert更多看-------连接
6,两个效率更高的插入方法:
1》
template <class... Args>
pair<iterator,bool> emplace (Args&&... args);
- 当该方法将键值对成功插入到 map 容器中时,其返回的迭代器指向该新插入的键值对,同时 bool 变量的值为 true;
- 当插入失败时,则表明 map 容器中存在具有相同键的键值对,此时返回的迭代器指向此具有相同键的键值对,同时 bool 变量的值为 false。
- 返回类型同insert
2》
template <class... Args>
iterator emplace_hint (const_iterator position, Args&&... args);
- 该方法不仅要传入创建键值对所需要的数据,还需要传入一个迭代器作为第一个参数,指明要插入的位置(新键值对键会插入到该迭代器指向的键值对的前面);
- 该方法的返回值是一个迭代器,而不再是 pair 对象。当成功插入新键值对时,返回的迭代器指向新插入的键值对;反之,如果插入失败,则表明 map 容器中存有相同键的键值对,返回的迭代器就指向这个键值对。
3. 成功,返回一个指向键值对的迭代器。
int main()
{map<string,int>mymap;pair<map<string,int>::iterator,bool> ret=mymap.emplace("China",1);cout<<ret.first->first<<" "<<ret.first->second<<endl;map<string,int>::iterator ite=mymap.emplace_hint(mymap.end(),"Americ",2);cout<<ite->first<<" "<<ite->second<<endl;return 0;
}
map访问值的方法
1,迭代器指向second对象:ite-second;
2,迭代器解引用之后引用second:(*ite).second;
3,下标访问(键作为下标访问):
需要特别注意的是:这个下标是键值,而不一定是0,1,2这些数字,。
eg:
mymap["YunNan"]
因为map是可以修改值的值的,所以:
mymap["YunNan"]="sanqizhixiang"
注意,只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值;反之,若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对。其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 "",即空字符串(即使用该类型的默认值作为键值对的值)。
4,除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。
mymap.at("YunNan");
注意:键和值的类型任意,不是键只能为int。
[ ]和at()
[ ]:
#include <iostream>
#include <map>int main() {std::map<std::string, int> my_map;my_map["apple"] = 10;my_map.insert(std::pair<std::string, int>("banana", 20));my_map.insert(std::make_pair("orange", 30));my_map.insert({"grape", 40});// Accessing values using [] operatorstd::cout << my_map["apple"] << std::endl;// Accessing values using at() methodstd::cout << my_map.at("banana") << std::endl;// Accessing values using find() methodauto it = my_map.find("orange");if (it != my_map.end()) {std::cout << it->second << std::endl;}// Accessing values using iteratorfor (auto it = my_map.begin(); it != my_map.end(); ++it) {std::cout << it->first << " : " << it->second << std::endl;}// Accessing values not existing in the map// using [] operatorstd::cout << my_map["mango"] << std::endl; // This will throw an exceptionif (my_map.find("mango") != my_map.end()) {std::cout << "exist mango in map" << std::endl;}return 0;
}
at():
#include <iostream>
#include <map>int main() {std::map<std::string, int> my_map;my_map["apple"] = 10;my_map.insert(std::pair<std::string, int>("banana", 20));my_map.insert(std::make_pair("orange", 30));my_map.insert({"grape", 40});// Accessing values using [] operatorstd::cout << my_map["apple"] << std::endl;// Accessing values using at() methodstd::cout << my_map.at("banana") << std::endl;// Accessing values using find() methodauto it = my_map.find("orange");if (it != my_map.end()) {std::cout << it->second << std::endl;}// Accessing values using iteratorfor (auto it = my_map.begin(); it != my_map.end(); ++it) {std::cout << it->first << " : " << it->second << std::endl;}// Accessing values not existing in the map// using at() methodstd::cout << my_map.at("mango")<< std::endl; // This will also throw an exceptionif (my_map.find("mango") != my_map.end()) {std::cout << "exist mango in map" << std::endl;} else {std::cout << "not exist mango in map" << std::endl;}return 0;
}
直接漰溃:
map和multimap
map | multimap | unordered_map | unorder_multimap | |
下标(键) | 支持 | 不支持(原因:因为键值可以重复) | 不支持 | |
map和unorder_map的区别
链接
map的部分函数
at() | 参数:键 |
find | 参数:键 成功返回元素迭代器,失败返回end()的迭代器。 |
成员方法 | 功能 |
---|---|
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 属性,不能用于修改容器内存储的键值对。 |
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
contains(key) | 注意:这个成员函数是c++20新增加的,必须是在使用C++20的情况下才能使用。 |
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 异常。 |
insert() | 向 map 容器中插入键值对。 |
erase() | 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。 可以是键值,也可以是迭代器: |
swap() | 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 |
clear() | 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。 |
emplace() | 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
emplace_hint() | 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
count(key) | 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 |
insert
源码
typedef std::pair<const _Key, _Tp> value_type;insert(value_type&& __x){ return _M_t._M_insert_unique(std::move(__x)); }template<typename _Pair>
__enable_if_t<is_constructible<value_type, _Pair>::value, pair<iterator, bool>>
insert(_Pair&& __x)
{
......
}
insert的插入不能直接map.insert(key,value),
需要插入一个pair对,也就是必须要使用{}阔起来。
map.insert({key,value});
map的下标和解引用
其他可以进行下标和解引用的容器,解引用和下标操作结果都是取值。
但是map不同,解引用是pair对象的引用,下标才是取值。
对于下标的使用要非常注意:如果不想因为所对应的键值对不存在而创建多余键值对,就不要用下标,应该用find查找:
map的一些操作
1,删除值
2,对map进行排序
首先一定要注意map模板类的第三个模板参数,这个参数决定元素按键值升序或者降序在map中的存储方式:
默认:less<key>升序
可设置:greater<key>降序
也可以自己定义键值比较函数,用函数名作为第三个参数
这个参数默认map 中的元素是以键值key递增存储的。
如果需要递减存储就设置第三个参数未greater<key>。
注意,key是你设置的数据的类型。
以上是初始建立map对象时设置的,如果运用过程中想要修改map容器的元素的存储顺序,怎么办:
自定义键值比较函数
详细链接
map中当key是结构体或者类时,需要重载哪些函数
看链接可知:函数对象中实现对函数调用符--小括号的重载。而map传递过去用于比较的是map的键,但是如果键是结构体或者类,那么结果或者类是比较不了的,所以这个时候需要我们自己重定义这个函数对象,重载()的时候,不是比较结构体或者类,而是比较结构体或者类中的某一个元素。
map实现按值排列存储---实现pair对存储数据
map实现不了按值存储,即使你实现了pair对按序插入,也不会按序在map中存储。我们只能将map中的pair对存储在vector中,让后自定义排序函数对pair对进行排序。
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;typedef pair<string, int> PAIR;bool cmp_by_value(const PAIR& lhs, const PAIR& rhs) {return lhs.second < rhs.second;
}struct CmpByValue {bool operator()(const PAIR& lhs, const PAIR& rhs) {return lhs.second < rhs.second;}
};int main() {map<string, int> name_score_map;name_score_map["LiMin"] = 90;name_score_map["ZiLinMi"] = 79;name_score_map["BoB"] = 92;name_score_map.insert(make_pair("Bing",99));name_score_map.insert(make_pair("Albert",86));//把map中元素转存到vector中vector<PAIR> name_score_vec(name_score_map.begin(), name_score_map.end());sort(name_score_vec.begin(), name_score_vec.end(), CmpByValue());// sort(name_score_vec.begin(), name_score_vec.end(), cmp_by_value);for (int i = 0; i != name_score_vec.size(); ++i){cout<<name_score_vec[i].first<<name_score_vec[i].second<<endl;}return 0;
}