c++中的关联容器主要是map、set,已经multimap、multiset。
为了讲map,得先将pair类型:pair就是一个两个类型的组合,比如一个人的学号就可以是pair<int,string>,其中的int是学号,string是名字。
map就是一个容器,里面装的就是若干个pair。每个pair的第一个元素,称为键(注意键的类型必须支持小于(<)操作!),第二个元素,称为值。对于普通的map,每个键都对应一个值。这样的看起来,键类似于数组的下标,而值类似于数组的值。map类定义了3种类型,分别为key_type、mapped_type、以及vaule_type。他们分别表示了键的类型、值的类型,以及一个pair类型,pair的第一个元素是键,第二个元素是值。
首先讲讲如何给imap添加元素:有两种的方法,使用键(下标)或者使用insert
使用下标这种方法与vector等类型的容器相矛盾。如果是一个空的vector,则不能使用下标直接访问,必须pusn_back进元素才行,直接访问会报错。而对于map如果没有这个键(下标),则会自动的向map中添加这个键,值为0。下标操作的返回值,就是这个键关联的值。利用这个性质,我们可以很方便的完成统计的功能,举个例子:
int main() { string str; map<string,int> wordCount; while(cin>>str) { ++wordCount[str]; } map<string,int>::iterator it_map = wordCount.begin(); cout<<"word"<<"\t\t"<<"count"<<endl; for(;it_map != wordCount.end();++it_map) cout<<it_map->first<<"\t\t"<<it_map->second<<endl; return 0; }
使用时如果键不存在,则赋值为0,然后对其自增。即变成了1。
insert方法有多个重载函数,表明你插入的是一个pair,还是一对迭代器指明的若干个pair,还是插入一个pair到指定的位置,但通常使用的插入一个pair,这个函数最关键是它的返回值。这个返回值也是一个pair,pair的第一个元素是指向该map类型的迭代器,另一个是bool类型的变量,表明插入成功与否。可以用insert重写上面的程序:
int main() { string str; map<string,int> wordCount; while(cin>>str) { //对于每个单词,都尝试去插入它 pair<map<string,int>::iterator,bool>ret = wordCount.insert(make_pair(str,1)); //通过检测返回值来判断插入是否成功 if(!ret.second) //插入失败表明map中有这个单词,只需要把对应键的值自增即可 ++ret.first->second; } map<string,int>::iterator it_map = wordCount.begin(); cout<<"word"<<"\t\t"<<"count"<<endl; for(;it_map != wordCount.end();++it_map) cout<<it_map->first<<"\t\t"<<it_map->second<<endl; return 0; }
那么如何读取map的元素呢?虽然我们也可以使用键来读取,但是潜在的问题是如果该键不存在,就会自动创建,这个特点并不是我们所希望的。我们可以通过count或者find函数来查找某个键是否存在。这两个函数的区别在于count返回的是出现的次数(对于map只能为0或者1),而find则返回的是指向该键的迭代器(如果没有找到,返回超末端迭代器:.end())。这意味着如果你是为为了统计是否存在,使用count就可以了,如果你找到某个元素并且还想使用它,那么find会比较合适。
删除元素使用使用erase操作,这与顺序容器差别不大。但是要注意的是,关联容器的顺序是按照“键”排列的。
下面通过一个小程序来说明:
#include <iostream> #include <map> #include <string> #include <vector> using namespace std; int main() { string str; map<string,int> wordCount; while(cin>>str) { //对于每个单词,都尝试去插入它 pair<map<string,int>::iterator,bool>ret = wordCount.insert(make_pair(str,1)); //通过检测返回值来判断插入是否成功 if(!ret.second) //插入失败表明map中有这个单词,只需要把对应键的值自增即可 ++ret.first->second; } map<string,int>::iterator it_map = wordCount.begin(); cout<<"word"<<"\t\t"<<"count"<<endl; for(;it_map != wordCount.end();++it_map) cout<<it_map->first<<"\t\t"<<it_map->second<<endl; //count方法:对于map,返回1或者0 int cnt = 0; cnt = wordCount.count("bird"); cout<<"cnt"<<cnt<<endl; //find方法:返回的是指向键的迭代器 int occurs = 0; map<string,int>::iterator it = wordCount.find("bird"); if(it != wordCount.end()) occurs = it->second; cout<<"occurs = "<<occurs<<endl; //删除元素 int del; string s1 = "hate"; //使用值删除 del = wordCount.erase(s1); if(del) cout<<s1<<" has been removed! "<<endl; else cout<<"can't find the word! "<<endl; //使用迭代器删除 map<string,int>::iterator iter = wordCount.begin(); wordCount.erase(iter); return 0; }
说完了map,我们在看看set。set只有键,没有值,所以他的vaule_type不是pair类型,而是key_type类型。同样的,它也支持insert、find、count操作。map比较适合于储存键值对应的情况,而set适合于储存键,判断键在不在这个集合中,比如黑名单之类的。
前面两种关联容器的特点是,键与值的对应关系是唯一的。而multimap或者multiset则支持一对多的关系。而且对于相同的键,总是连续的存储。由于这种一对多关系,所以multimap和multiset支持一些map和set没有的操作。比如lower_bound、upper_bound以及equal_range.它们分别返回的是指向某个键的第一个元素,最后一个元素的下一个元素以及这连个元素组成的范围。看一个综合例子:
#include <iostream> #include <map> #include <string> using namespace std; int main() { multimap<string,string> authors; string author,work,searchItem; //建立作者及其作品的容器 do { cout<<"enter authors name"<<endl; cin>>author; if(!cin) break; cout<<"enter authors works"<<endl; while(cin>>work) authors.insert(make_pair(author,work)); cin.clear(); }while(cin); cin.clear(); //删除元素 cout<<"who is the author that you want erase:"<<endl; cin>>searchItem; /* //使用erase删除:输出对应键的所有值 multimap<string,string>::iterator iter = authors.find(searchItem); if(iter != authors.end()) authors.erase(searchItem); else cout<<"cannot find the author!"<<endl; */ //使用equal_range或得迭代器删除 typedef multimap<string,string>::iterator itType; pair<itType,itType> pos = authors.equal_range(searchItem); if(pos.first != pos.second) authors.erase(pos.first,pos.second); else cout<<"can not find this author!"<<endl; //输出删除结果 cout<<"author\t\twork:"<<endl; multimap<string,string>::iterator itbegin = authors.begin(); for(;itbegin != authors.end();++itbegin) cout<<itbegin->first<<"\t\t"<<itbegin->second<<endl; return 0; }