map—要求不重复关键字、有序存储的关键字-值对的集合。
set—要求不重复关键字、有序存储的集合。
两者都是模板类。
map中的元素是一些关键字-值对:关键字起到索引的作用,值表示与索引相关联的数据。set中每个元素只包含一个关键字:set支持高效的查询操作—检查一个给定关键字是否在set中。
定义:
当定义一个map时,必须既指名关键字类型有指名值类型;而定义一个set时,只需要指名关键字类型,因为set中没有值。每个关联容器都定义了一个默认的构造函数,它创建一个指定类型的空容器。我们也可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器,只要这些值可以转化为容器所需类型就可以。在新标准下,我们也可以对关联容器进行值初始化。
#include<map>
#include<set>
map<T1,T2> m;
set<T> s;
map<T1,T2>m=m1;
map<T1,T2>m(m1);
set<T> s=s1;
set<T> s(s1);
//列表初始化只适用于C++11标准
map<T1,T2>m={a,b,c..};
set<T> s={a,b,c..};
关键字类型的要求:
关联容器对其关键字类型有一些限制。
对于有序容器,关键字必须定义元素比较的方法。默认情况下,标准库使用关键字类型的<运算符来比较两个关键字。在集合类型中,关键字就是元素类型;在映射类型中,关键字类型是元素的第一部分的类型。
为了指定使用自定的操作来代替关键字上的<运算符,必须在定义关联容器类型时提供此操作的类型。用尖括号指出要定义哪种类型的容器,自定义的操作类型必须在尖括号中紧跟着元素类型给出。
所以当我们定义并使用了一个关联容器时,它本身就是“从小到大”排序的。(从begin()到end(),小:严格弱序)。
pair类型:
pair有两个成员,分别命名为first和second。pair的默构造函数会对first和second成员进行值初始化。
#include<utility>
pair<T1,T2>;//p是一个pair,两个类型分别为T1和T2的成员都进行了值初始化
make_pair(v1,v2);//返回一个用v1和v2初始化的pair。pair的类型是v1和v2的类型推断出来
关联容器操作:
key_type //此容器类型的关键字类型
mapped_type //只适用于map,每个关键字关联的类型
value_type //对于set,与key_type相同
//对于map,为pair<const key_type,mapped_type>
关联容器迭代器:
当解引用一个关联容器时,我们会得到一个类型为容器的value_type的值的引用。对map而言,value_type是一个pair类型,其first成员保存const的关键字,second成员保存值。
虽然set类型同时定义了iterator和const_iterator类型,但两种类型都只允许访问set中的元素。与不能改变map元素的关键字一样,一个set中的关键字也是const的。 可以用一个set迭代器来读取元素的值,但不能修改。
map<string,int> word_count;
map<string.int>::iterator map_it=word_count.begin();
cout<<map_it->first<<" "<<map_it->second;
map_it->first="new key";//错误,map的关键字是const的
++map_it->second;
set<int> iset;
set<int>::iterator set_it=iset.begin();
if(set_it != iset.end(){
*set_it = 42;//错误,set中的关键字是const的
cout<<*set_it<<endl;
}
添加元素:
//v是一个value_type类型的对象。
c.insert(v);//对于map和set,只有当元素的关键字不在c中时才插入元素。
//函数返回一个pair,包含一个迭代器,
//first成员是一个迭代器指向具有指定关键字的元素,
//second成员是一个bool值,指出元素是插入成功还是已经存在于容器中
//如果关键字已在容器中,则inset什么事也不做,且返回值中的bool部分为false
//如果关键字不存在,元素被插入容器中,且bool值为true
word_count.insert(make_pair("new key",1));
删除元素:
关联容器提供一个额外的erase操作,它接受一个key_type参数,此版本返回所有匹配给定关键字的元素(如果存在的话),返回实际删除元素的数量。对于map和set,erase的值总是0或1;允许重复关键字的容器,删除元素的数量可能大于1。
c.erase(p);//从c中删除迭代器p指定的元素,返回一个指向p之后元素的迭代器
c.erase(k);//从c中删除所有关键字为k的元素,返回一个size_type值,指出删除元素的数量
map的下标操作:
set不支持下标,因为set中没有与关键字相关联的“值”。我们不能对一个multimap或一个unordered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。
map的下标运算符与我们用过的其他下标运算符的一个不同之处是其返回类型。通常情况下,解引用一个迭代器所返回的类型与下标运算符返回的类型是一样的。但对map则不然:当对一个map进行下标操作时,会得到一个mapped_type对象;但当解引用一个map迭代器时,会得到一个value_type对象。
map下标运算符接受一个索引(即一个关键字),获取与此关键字相关联的值。但是,如果关键字不在map中,会为此关键字创建一个元素并插入到map中,关联值将进行值初始化。
由于下标运算符可能插入一个新元素,我们只能对非const的map使用下标操作。
c[k];//返回关键字为k的元素对应的值的 **引用** :如果k不在c中,添加一个关键字为k的元素,对其进行值初始化
c.at(k);//访问关键字为k的元素,带参数检查,若k不在c中则抛出一个out_of_range异常
访问元素:
下标运算符提供了最简单的提取元素的方法,但有时如我们所见,使用下标运算符有一个严重的副作用:如果关键字还未在map中,下标操作会插入一个具有给定关键字的元素。但有时,我们只想知道一个给定关键字是否在map中,而不想改变map。这样就不能使用下标运算符来检查一个元素是否存在,因为如果关键字不存在的,下边运算符会插入一个新元素。这种情况下,应该使用find。
c.find(k);//返回一个迭代器,指向第一个关键字为k的元素,若k不在c中,则返回尾后迭代器
c.count(k);//返回关键字等于k的元素的数量,对于不允许关键字重复的容器,返回值永远是0或1
在multiset和multimap中查找元素:
c.lower_bound(k);//,返回一个迭代器,若找到,指向第一个关键字不小于k的元素
c.upper_bound(k);//返回一个迭代器,若找到,指向第一个关键字大于k的元素,
//即最后一个关键字为k的元素的下一个元素的迭代器。
//若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置
c.equal_range(k);//返回一个迭代器pair,若关键字存在,则第一个迭代器指向
//第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置
//若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置