您的位置:首页 > 编程语言 > C语言/C++

C++ primer第二次阅读学习笔记(第10章:关联容器)

2012-05-21 17:43 239 查看

第十章:关联容器

关联容器和顺序容器的本质差别在于:关联容器通过键存储和读取元素,而顺序元素通过元素在容器中的位置顺序存储和访问元素。虽然关联容器的大部分行为与顺序容器相同,但其独特之处在于支持键的使用。
关联容器通过键来高效的查找和读取元素,两个基本的关联容器为map和set。map的元素以键---值对的形式组织,键用作元素在map中的索引,而值表示存储和读取的元素。set仅包含一个键,并有效地支持关于某个键是否存在的查询。
如果希望存储不同值的集合,使用set比较合适,而map容器更适用于需要存储每个键所关联的值得情况。如在做文本处理时,可使用set保存忽略的单词,而字典则是map的一种很好的应用。单词本身是键,而释义则是值。
pair包含两个数值,是一种模板类型。在utility头文件中定义。在创建pair类型时必须提供两个类型名。这两个类型不必相同。如
1:pair<T1,T2> p1;
2:pair<T1,T2> p1(v1,v2);
3:make_pair(v1,v2);//以v1,v2的值创建新的pair对象。
如果在创建pair对象时不提供初始化式,则调用默认构造函数对其成员进行初始化。
pair类型的使用很繁琐,可以使用typedef简化其声明:typedef pari<string,string> author;
author A("jim","green");
可以直接访问pair对象数据成员,其成员都是公有的。分别命名为first和second。
关联容器共享大部分但并非全部顺序容器操作。如
C<T> c;
C<T> c1(c2);
C<T>c(b,e);//b,e为一对迭代器。
关联容器无法通过容器大小来定义,因为那样就无法知道键对应的值是什么。
容器的元素根据次序排列,因此在迭代器遍历关联容器时,我们可确保键的顺序访问元素而与元素在容器的存放位置完全无关。
map可以理解为关联数组。可使用键作为下标来获取一个值。
要使用map对象必须使用map头文件。同时必须分别指明键和值的类型。在使用关联容器时,键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义<操作符来实现键的比较。可以通过重写默认的操作符,并提供自定义的操作符函数,在后面的模板中会有介绍。所用的比较函数必须在键类型定义严格弱排序。
对于键类型,唯一的要求就是必须支持<操作符,至于是否支持其他的关系或相等运算则不作要求。如不能定义一个list<int>::iterator关联int对象,因为list的iterator仅支持除==和!=运算符,不支持<运算符(list中的元素不是顺序存储,存储位置不能作为在list中的相对位置)。而vector 和deque却可以。
map对象的元素是键--值对。它包含两个部分:键以及键关联的值。map的value_type就反映这个事实。value_type就是存储元素的键以及值得pair类型,而且键为const。
map(T1,T2>::value_type map对象元素类型。pari<T1,T2>
map(T1,T2>::mapped_type 键关联的值得类型T2。
map(T1,T2>::key_type 用作索引的键的类型T1。
对map迭代器进行解引用将获得一个引用,指向容器中一个value_type类型的值,其为pair。
pair的first存放键,但它为const,second成员存放值。
可以通过insert向map容器中添加元素。也可以通过键作为下标获取元素,然后给获取的元素赋值。
通过以键作为下标访问map对象。
map<string,int>word_count;
word_cout["Jim"]=1;
将发生以下事情:
1:在word_count中查找键名为Jim的元素,没有找到。
2:将一个新的键值对插入到word_count中。它的键是const string类型,保存Jim,而它的值采用值初始化,为0;
3:将这个新的pair对象插入word_count。
4:读取新插入的元素,修改为1;
因此使用键作为下标来访问不存在的元素将会导致在map容器中添加新的元素。所关联的值采取值初始化,类类型的元素用默认构造函数初始化,而内置类型将会被初始化为0。如果该键已存在则map的下标运算与vector的下标运算行为相同。
map::insert成员函数使用。
m.insert(e);//e为pair对象。
m.insert(beg,end);//beg和end为一对迭代器
m.insert(iter,e);//e为pair对象。iter为迭代器。
1:word_count.insert(map<string,int>::value_type("Jim",1));
2:word_count.insert(make_pair<string,int>("Jim",1));
3:word_count.insert(pair<string,int>("Jim",1));
其中1与3大同小异。因为value_type就是由pair<string,int>定义而来。
使用insert可以避免下标操作符带来的副作用:不必要的初始化。
当要插入的元素的键值已经存在,则不作任何操作。因此当传递一对迭代器时,并不说明该迭代器范围内的元素都被插入。
m.insert(e)将返回一个pair类型的对象。first为一迭代器,指向map中具有相应键的元素(pair类型),second为bool类型。如果该键已在容器中,则返回的bool为false,如该键不在容器中,则插入新元素,bool值为true。即bool指示是否插入该元素到容器中。这两种情况下返回的迭代器都指向具有给定键的元素(pair类型)。如
map<string,int> word_count;
pari<map<string,int>::iterator,bool>ret
=word_count.insert(make_pari("Jim",1));
m.count(k);//返回m中k出现的次数。
m.find(k),//如果存在以k为索引的元素则返回指向该元素的迭代器,否则指向超出末端迭代器。
如:map<string,int>::iterator it=word_count.find("Jim");
if(it!==word_count.end())
//存在。
因为map为单值映射,因此count的返回值非0即1.这可以用于判断某个键是否存在。而对于multimap,count的返回值则可能会>1;
从map容器中删除元素的erase操作有三种变化形式:
1:m.erase(k);//删除键为k的元素,返回删除个数。
2:m.erase(p);//删除迭代器p所指向的元素,返回void。
3:m.erase(b,e);//删除一段迭代器范围内元素。返回void。
map容器是键--值对的集合,好比是以人名为键的地址和电话号码。而set仅是单纯键的集合。
为了使用set必须包含set头文件,set不支持下标操作符,没有定义mapped_type类型,value_type是与key_type相同的类型。都是指容器中的元素类型,且键值必须唯一不能修改。
set容器中的元素必须唯一,如果以一段范围的迭代器初始化set容器,对于每个键,只添加一个元素,重复的元素不再添加。如
char *name={"Jim","Hellen","Green","Tom","Kate","Jim","Tom"};
set<string> Name(name,name+7);
对于此句由于Jim和Tom均出现两次,但插入到set容器内时,重复元素不会被添加多次。此时set有5个元素。
在set内添加元素使用insert,与map类似。
1:m.insert(a);//a为键值。返回pair对象与map相同。
2:m.insert(b,e);//b,e为迭代器范围。
set容器不提供下标操作符,要从容器内获取元素可以使用find运算,它返回指向该元素的迭代器(如果存在的话)。在获得某元素的迭代器之后只能对其做读操作不能修改,因为键是const类型的。
count返回值只能为0或1。
各种容器的许多操作都是类似的,C++primer在介绍set时省略了很多,详细用法请参考其他资料。
multimap和multiset
在map和set容器中一个键只能对应一个实例。而multimap和multiset则允许一个键对应多个实例。如在电话簿中,每个人可能有单独的电话号码列表。multimap和multiset与map和set具有相同的头文件分别为set和map。
multimap不支持下标运算,这一点是个例外。其他操作multimap和multiset与map和set操作相同。
insert和erase同样适用于multiset和multimap。
erase将删除拥有该键的所有元素,并返回删除元素个数。
关联容器的元素是按顺序存放的,而具有相同键的元素在容器中相邻存放。这一点非常重要。如

multimap<string,string>::iterator iter=author.find("Jim");
for(unsigned i=0;i!=author.count();i++)
{
cout<<iter->second<<endl;
iter++;

}
首先确定键为Jim的元素个数。然后获得指向第一个该键所关联的元素的迭代器。依次获得具有该键的所有元素。因此明白:在multimap中同一个键所关联的元素必然相邻存放是突破点。
另一个替代方案为:
m.lower_bound(k);//返回迭代器,指向键不小于k的第一个元素。
m.upper_bound(k);//返回迭代器,指向键大k的第一个元素
m.equal_bound(k);//返回pair对象,first成员等价于m.lower_bound(k),而second成员等价于m.upper_bound(k);
所有这些操作都需要传递一个键。
在同一个键上调用lower_bound和upper_bound将产生一个迭代器范围,指出该键所关联的所有元素。如该键在容器中存在,则将会获得两个不同的迭代器。如果该键不存在,这两个操作将指向同一个迭代器,指向依据元素的排列顺序应该插入的位置。
调用equal_range可以取代lower_bound和upper_bound函数。它返回存储一对迭代器的pair对象。如果该值存在,则pair对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素,则pair对象中的两个迭代器都将指向此键应插入的位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: