关联容器支持高效地关键字查找和访问。两个主要地关联容器(associative-container)类型是map和set。
map中的元素是一些关键字-值(key-value)对。关键字起到索引的作用,值则表示与索引相关联的数据。
set中每个元素包含一个关键字;set支持高效地关键值查询操作 -- 检查一个给定关键字是否在set中。
类型map和multimap定义在头文件map中;set和multiset定义在头文件set中;
无序容器则定义在头文件unordered_map和unordered_set中
关联容器类型 | |
按关键字有序保存元素(有序容器) | |
map | 关联数组:保存关键字-值对 |
set | 关键字即值,即只保存关键字的容器 |
multimap | 关键字可重复出现的map |
multiset | 关键字可重复出现的set |
无序容器 | |
unordered_map | 用哈希函数组织的map |
unordered_set | 用哈希函数组织的set |
unordered_multimap | 哈希组织的map;关键字可以重复出现 |
unordered_multiset | 哈希组织的set;关键字可以重复出现 |
map是关键字-值对组合,map通常被称为关联数组。
例如:单次计数
#include <map>
#include <iostream>
/*map*/
using namespace std;
int main()
{
map<string,size_t> word_count;
string word;
while(cin >> word){
++word_count[word];
}
for(auto i : word_count)
cout << i.first << " occur " << i.second << " times" << endl;
cout << "Hello World!" << endl;
return 0;
}
1、定义关联容器
定义一个map时,必须指明关键字类型和值类型
map<string ,size_t> word_count; //空容器
map<string ,string> authorInfo = {{"phone","150XXXX9410"},{"name","utotao"}}
当初始化一个map时,必须提供关键字类型和值类型,将每一个关键值-对包围在花括号中:
{key , value}
来指出它们一起构成了map中的一个元素。在每个花括号中,关键值是第一个元素,值是第二个。
2、pair对
(1)头文件:
#include <utility>
(2)pair创建及初始化
当创建pair的时候,必须提供两个类型名
#include <utility>
#include <vector>
pair<string, string> anon{"name","utotao"};//初始化
pair<string,size_t> anon2;
pair<string,vector<string >> anon3;
(3)pair上的操作:
pair<T1, T2> p | p是一个pair,两个类型分别是T1和T2的成员都进行了初始化 |
pair<T1, T2> p(v1, v2) | p是一个成员类型为T1,T2的pair;first和second成员分别用v1和v2进行初始化 |
pair<T1, T2> p = {v1, v2} | 等价于pair<T1, T2> p(v1, v2) |
make_pair(v1,v2) | 返回一个用v1,v2初始化的pair。pair的类型是由v1和v2的类型推导而来的 |
p.first | 返回p的名为first(公有)数据成员 |
p.second | 返回p的名为second(公有)数据成员 |
p1 relop p2 | 关系运算符(>,>=,<,<=)按字典序定义 |
p1 == p2 | 当first和second成员分别都相等的时候,p1等于p2;否则,不相等 |
p1 != p2 |
3、关联容器操作
(1) 关联容器额外的类型别名
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型:只适用于map |
value_type |
对于set,与key_type相同 对于map,为pair<const key_type , map_type> |
对于set类型,key_type和value_type是一样的;set中保存的值就是关键字。
在map中,元素是关键字-值对。即,每个元素是一个pair对象,包含一个关键字和一个关联的值。由于不能改变这些关键字,因此这些pair的关键字部分是const的。
(2) 关联容器迭代器
对于map来说,value_type是一个pair对,其first成员保存const关键字,second保存成员值。
#include <map>
#include <iostream>
/*map*/
using namespace std;
int main()
{
map<string,size_t> word_count;
string word;
while(cin >> word){
++word_count[word];
}
for(auto i : word_count)
cout << i.first << " occur " << i.second << " times" << endl;
cout << "==============" << endl;
auto map_it = word_count.begin();
cout << (*map_it).first << endl;
cout << (*map_it).second << endl;
return 0;
}
(3) 添加元素
向set中添加元素:
vector<int > ivec = {2,4,6,8,2,4,6,8};
set<int > set2;
set2.insert(ivec.cbegin(),ivec.cend());
set2.insert({1,3,5,7,1,3,5,7});
向map中添加元素:
//向word_count插入word的4种方法
word_count.insert({word,1});
word_count.insert(make_pair(word,1));
word_count.insert(pair<string,size_t>(word,1));
word_count.insert(map<string,size_t>::value_type(word,1));
在新标准下,创建一个pair最简单的方法就是在参数列表中使用花括号初始化,也可以调用make_pair或显式构造pair。
c.insert(v) |
v是value_type类型的对象;args用来构造一个元素 |
c.emplace(args) | 对于map和set,只有当元素的关键字不在c中时才插入(或构造)元素。函数返回一个pair,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool值。 |
c.insert(b ,e) |
b和e是迭代器,表示一个c::value_type类型值的范围;i1是这种值的花括号列表。函数返回void 对于map和set,只插入关键字不在c中的元素。对于multimap和multiset,则会插入范围中的每个元素 |
c.insert(i1) | |
c.insert(p , v) | 类似insert(v)(或emplace(args)),但迭代器p作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素 |
c.emplace(p, args) |
insert返回值:对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair。pair的第一个元素first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中。如果关键字已经存在于容器之中,则insert什么事情也不做,且返回值中的bool部分为false。如果关键值不存在,元素被插入容器中,且bool值为true。
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
string word;
map<string,size_t> word_count;
while(cin >> word){
auto ret = word_count.insert({word,1});
if(!ret.second)
++ret.first->second;
}
for(auto i : word_count)
cout << i.first << " " << i.second << endl;
cout << "Hello World!" << endl;
return 0;
}
if语句检查返回值的bool部分,若为false,则表明插入未发生。在此情况下,word已经存在于word_count之中,因此必须递增此元素所关联的计数器。
(4) 删除元素
关联容器顶一个三个版本的erase操作函数:
- 传入一个迭代器,删除指定的元素
- 传入一个迭代器范围,删除指定范围的元素
- 接受一个key_value参数,删除所匹配给定关键字的元素(如果存在的话),返回实际删除元素的数量
c.erase(k) | 从c中删除每一个关键字为k的元素。返回一个size_type值,指出删除的元素的数量 |
c.erase(p) | 从c中删除迭代器p指定的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回c.end() |
c.erase(b,e) | 删除迭代器对b和e所表示的范围中的元素。返回e |
注意:当解引用一个关联容器迭代器的时候,所得到的是一个类型为容器的value_type的值的引用。
例子:删除关键字为hello的元素
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
string word;
map<string,size_t> word_count;
while(cin >> word){
auto ret = word_count.insert({word,1});
if(!ret.second)
++ret.first->second;
}
cout << "==============" << endl;
for(auto i : word_count)
cout << i.first << " " << i.second << endl;
word_count.erase("hello");
cout << "===== after erase hello =====" << endl;
for(auto i : word_count)
cout << i.first << " " << i.second << endl;
cout << "===== after erase iterate =====" << endl;
word_count.erase(word_count.cbegin(),prev(word_count.cend()));
for(auto i : word_count)
cout << i.first << " " << i.second << endl;
return 0;
}
(5) map下标操作和访问元素
① map下标操作:
map和unordered_map提供了下表操作,set没有提供下表操作,因为set没有与关键字(key)相对应的值。不能对multimap和unordered_multimap进行下标操作,因为这两种容器可能有多个值与通过一个关键字相关联。
对map使用下标操作(索引为关键字):
case 1 : 容器中有对应的关键字,返回与之相关联的值
case 2 : 容器中没有该关键字,创建该元素并插入到map中,关联值将进行值的初始化
map和unordered_map使用下标操作 | |
c[k] | 返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其进行值的初始化 |
c.at[k] | 访问关键字为k的元素,带参数检查;若k不在c中,则抛出一个out_of_rage异常 |
② 访问元素:
在一个关联容器中查找元素的方法 | |
lower_bound和upper_bound不适用于无序容器 下标和at操作只适用于非const的map和unordered_map | |
c.find(k) | 返回一个迭代器,指向第一个关键字为k的元素;若k不在容器中,则返回尾后迭代器 |
c.count(k) | 返回关键字为k的元素数量。对于不允许重复关键字的容器,返回值永远是0或者1 |
c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k的元素 |
c.upper_bound(k) | 返回一个迭代器,指向第一个关键字大于k的元素 |
c.equal_range(k) |
返回一个迭代器pair,表示关键字等于k的元素范围。若k不存在,pair的两个成员均等于c.end() |