《C++ Primer》 第十一章 关联容器
11.1 使用关联容器
使用map:
//统计每个单词在输入中出现的次数
map<string, size_t> word_count;//string到size_t的空map
string word;
while(cin>>word)
++word_count[word];//提取word的计数器并将其加1
for(const auto &w: word_count)
cout<<w.first<<"occurs "<<((w.second>1)?"times":"time")<<endl;
使用set:
//统计每个单词在输入中出现的次数
map<string, size_t> word_count;//string到size_t的空map
set<string> exclude = {"The","But","And","Or","An","A",
"the","but","and","or","an","a"};
string word;
while(cin>>word)
//只统计不在exclude中的单词
if(exclude.find(word)==exclude.end())
++word_count[word];//获取并递增word的计数器
11.2 关联容器概述
关联容器的迭代器都是双向的,且不支持push_front或push_back。
定义关联容器:
//三个元素,authors将姓映射成名
map<string,string> authors = { {"Joyce", "James"},
{"Austen", "Jane"},
{"Dickens","Charles"}};
初始化multimap或multiset:允许多个元素具有相同的关键字
//定义一个有20个元素的vector,保存0~9的每个整数的两个拷贝
vector<int> ivec;
for(vector<int>::size_type i=0; i!=10; ++i){
ivec.push_back(i);
ivec.push_back(u);
}
//iset包含来自ivec的不重复的元素;miset包含所有20个元素
set<int> iset(ivec.cbegin(), ivec.cend());
multiset<int> miset(ivec.cbegin(), ivec.cend());
cout<<ivec.size()<<endl;//20
cout<<iset.size()<<endl;//10
cout<<miset.size()<<endl;//20
pair类型:定义在utility中,两个成员分别是first和second。
pair<string,string> anon; //保存两个string
pair<string, size_t> word_count; //保存一个string和一个size_t
pair<string,vector<int>> line; //保存string和vector<int>
//为每个成员提供初始化器
pair<string,string> author{"James", "Joyce"};
创建pair对象的函数:可以对返回值进行列表初始化
pair<string, int> process(vector<string> &v)
{
//处理v
if(!v.empty())
return {v.back(), v.back().size()};//列表初始化
else
return pair<string, int>();//隐式构造返回值
}
11.3 关联容器操作
由于不能改变一个元素的关键字,因此pair的关键字部分是const的。
关联容器迭代器:
//获得指向word_count中一个元素的迭代器
auto map_it = word_count.begin();
//*map_it是指向一个pair<const string, size_t>对象的引用
cout<<map_it->first;//打印此元素的关键字
cout<<" "<<map_it->second;//打印此元素的值
map_it->first = "new key";//错误:关键字是const的
++map_it->second;//正确:可以通过迭代器改变元素
set的迭代器是const的:可以用一个set迭代器来读取元素的值,但不能修改
set<int> iset = {0,1,2,3,4,5,6,7,8,9};
set<int>::iterator set_it = iset.begin();
if(set_it!=iset.end()){
*set_it=42;//错误:set中的关键字是只读的
cout<<*set_it<<endl;//正确:可以读关键字
}
遍历关联容器:
//获得一个指向首元素的迭代器
auto map_it = word_count.cbegin();
//比较当前迭代器和尾后迭代器
while(map_it!=word_count.cend()){
//解引用迭代器,打印关键字-值对
cout<<map_it->first<<" occurs "
<<map_it->second << " times "<<endl;
++map_it;//递增迭代器,移动到下一元素
}
添加元素:
vector<int> ivec = {2,4,6,8,2,4,6,8};
set<int> set2;
set2.insert(ivec.begin(), ivec.cend());//有4个元素
set2.insert({1,3,5,7,1,3,5,7});//set2现在有8个元素
向map添加元素:对一个map进行insert操作时,必须记住元素类型是pair。
//向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));
检测insert的返回值:
//统计每个单词在输入中出现次数的一种更繁琐的方法
map<string, size_t> word_count;//从string到size_t的空map
string word;
while(cin>>word){
//插入一个元素,关键字等于word,值为1
//若word已在word_count中,insert什么也不做
auto ret = word_count.insert({word,1});
if(!ret.second)//word已在word_count中
++ret.first->second;
}
向multiset或multimap添加元素
multimap<string,string> authors;
//插入第一个元素,关键字为Barth,John
authors.insert({"Barth,John", "Sot-Weed Factor"});
//正确:添加第二个元素,关键字也是Barth,John
authors.insert({"Barth,John","Lost in Funhouse"});
删除元素:
//删除一个关键字,返回删除的元素数量
if(word_count.serase(removal_word))
cout<<"ok: "<<removal_word<<" removed\n"
else
cout<<"oops: "<<removal_word<<" not found\n";
map的下标操作:由于下标运算符可能插入一个新元素,所以只对非const的map使用下标操作
访问元素:
set<int> iset={0,1,2,3,4,5,6,7,8};
iset.find(1);//返回一个迭代器,指向key==1的元素
iset.find(11);//返回一个迭代器,其值等于iset.end()
iset.count(1);//返回1
iset.count(11);//返回0
对map可以使用find代替下标操作:
if(word_count.find("foolbar") == word_count.end())
cout<<"foolbar is not in the map"<<endl;
在multimap或multiset中查找元素
string search_item("Alain de Botton");//要查找的作者
auto entries = authors.count(search_item);//元素的数量
auto iter = authors.find(search_item);//此作者的第一本书
//用一个循环查找此作者的所有著作
while(entries){
cout<<iter->second<<endl;
++iter;//前进到下一本书
--entries;//记录已经打印了多少本书
}
一种不同的,面向迭代器的解决方法:
for(auto beg = authors.lower_bound(search_item),
end = authors.upper_bound(search_item);
beg!=end;++beg)
cout<<beg->second<<endl;
equal_range函数:
//pos保存迭代器对,表示与关键字匹配的元素范围
for(auto pos = authors.equal_range(search_item);
pos.first!=pos.second;++pos.first)
cout<<pos.first->second<<endl;//打印每个题目
一个单词转换的map
void word_transform(ifstream &map_file, ifstream &input)
{
auto trans_map = buildMap(map_file);//保持转换规则
string text;//保存输入中的每一行
while(getline(input,text)){//读取一行输入
istringstream stream(text);//读取每个单词
string word;
bool firstword = true;//控制是否打印空格
while(stream>>word){
if(firstword)
firstword = false;
else
cout<<" ";//在单词间打印一个空格
//transform返回它的第一个参数或其转换之后的形式
cout<<transform(word,trans_map);//打印输出
}
cout<<endl;
}
}
//函数buildMap读入给定文件,建立起转换映射
map<string,string> buildMap(ifstream &map_file)
{
map<string,string> trans_map;//保存转换规则
string key;//要转换的单词
string value;//转换后的内容
//读取第一个单词存入key中,行中剩余内容存入value
while(map_file>>key&&getline(map_file,value))
if(value.size()>1) //检查是否有转换规则
trans_map[key]=value.substr(1);//跳过前导空格
else
throw runtime_error("no rule for "+key);
return trans_map;
}
//生成转换文本
const string & transform(const string &s, const map<string,string> &m)
{
auto map_it=m.find(s);
//如果单词在转换规则中
if(map_it!=m.cend())
return map_it->second;//使用替换短语
else
return s;//否则返回原string
}
11.4 无序容器
使用无序容器:
//统计出现次数,但单词不会按字典序排列
unordered_map<string,size_t> word_count;
string word;
while(cin>>word)
++word_count[word];//提取并递增word的计数器
for(const auto &w: word_count)//对map中的每个元素
cout<<w.first<<" occurs "<<w.second<<((w.second>1)?"times":"time")<<endl;
管理桶:
概念总结
vector和map的区别: