map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap类型则允许一个键对应多个实例。multimap 和 multiset 所支持的操作分别与 map 和 set 的操作相同,只有一个例外:multimap 不支持下标运算。不能对 multimap 对象使用下标操作,因为在这类容器中,某个键可能对应多个值。为了顺应一个键可以对应多个值这一性质,map 和 multimap,或 set 和 multiset 中相同的操作都以不同的方式做出了一定的修改。在使用 multimap 或 multiset 时,对于某个键,必须做好处理多个值的准备,而非只有单一的值。
元素添加和删除
insert 操作和 erase 操作同样适用于 multimap 和 multiset 容器,实现元素添加和删除。
由于键不要求是唯一的,因此每次调用 insert 总会添加一个元素。
//insert first element
authors.insert(make_pair( string("Au"), string("book1")));
//insert second element
authors.insert(make_pair( string("Au"), string("book2")));
带有一个键参数的 erase 版本将删除拥有该键的所有元素,并返回删除元素的个数。而带有一个或一对迭代器参数的版本只删除指定的元素, 并返回 void类型。
元素删除
关联容器 map 和 set 的元素是按顺序存储的。而 multimap 和multset 也一样。因此,在 multimap 和 multiset 容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。迭代遍历 multimap 或 multiset 容器时,可保证依次返回特定键所关联的所有元素。
在multimap 中查找元素有两种方法:
1. 使用find 和count操作
count 函数求出某键出现的次数, 而 find 操作则返回一个迭代器, 指向第一个拥有正在查找的键的实例,在后面展示的习题代码中有使用这种方法。
2. 使用关联容器 lower_bound 、upper_bound 和 equal_range。
表 1 返回迭代器的关联容器操作
m.lower_bound(k) | 返回一个迭代器,指向键不小于 k 的第一个元素 |
m.upper_bound(k) | 返回一个迭代器,指向键大于 k 的第一个元素 |
m.equal_range(k) | 返回一个迭代器的 pair 对象它的 first 成员等价于 m.lower_bound(k)。而 second 成员则等价于m.upper_bound(k) |
*
若该键没有关联的元素,则 lower_bound 和 upper_bound 返回相同的迭代器:都指向同一个元素或同时指向 multimap 的超出末端位置。它们都指向在保持容器元素顺序的前提下该键应被插入的位置。
使用equal_range 的效果和 low_bound+upper_bound 的效果一样。调用 equal_range 函数来取代调用 upper_bound 和 lower_bound 函数。equal_range 函数返回存储一对迭代器的 pair 对象。如果该值存在,则 pair 对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素,则 pair 对象中的两个迭代器都将指向此键应该插入的位置。
习题程序代码
Exercise 10.26 编写程序建立作者及其作品的 multimap 容器。使用find 函数在multimap 中查找元素,并调用 erase 将其删除。当所寻找的元素不存在时,确保你的程序依然能正确执行。
Exercise 10.27 重复上一题所编写的程序,但这一次要求使用equal_range 函数获取迭代器,然后删除一段范围内的元素。
Exercise 10.28 沿用上题中的 multimap 容器,编写程序以下面的格式按姓名首字母的顺序输出作者名字:
Author Names Beginning with ‘A’:
Author, book, book, …
…
Author Names Beginning with ‘B’:
…
将以上三题编写到了一个程序中:
#include<iostream>
#include<fstream>
#include<map>
#include<algorithm>
using namespace std;
//open files
ifstream &open_file(ifstream &in, const string file){
in.close();
in.clear();
in.open(file.c_str());
return in;
}
//read files into multimap
void authors_works(ifstream &authorWorks, multimap<string, string> &links){
string author, work;
while(authorWorks >> author >> work)
links.insert(make_pair(author, work));
}
int main(int argc, char *argv[]){
ifstream authorWorks;
multimap<string, string> links;
if(argc < 2)
throw range_error("NO files input!");
if(!open_file(authorWorks, argv[1]))
throw runtime_error("can not open file!");
authors_works(authorWorks, links);
string search_author;
//search multimap by iterm using count and find
cout << "Input your search author: ";
cin >> search_author;
typedef multimap<string, string>::size_type sz_type;
typedef multimap<string, string>::iterator multi_iter;
sz_type entries = links.count(search_author);
multi_iter iter = links.find(search_author);
if(entries == 0)
cout << "There is no enty for this author. " << endl;
else {
for(sz_type i = 0; i != entries; ++i, ++iter)
cout << iter->second << " ";
cout << endl;
cout << "This entry will be deleted!" << endl;
sz_type n = 0;
if(n = links.erase(search_author))
cout << "delete " << n << " about the author successfully!" << endl;
else
cout << " delete error!";
}
//search multimap by iterator using equal_range
cout << "Input another author: ";
cin >> search_author;
pair<multi_iter, multi_iter> pos = links.equal_range(search_author);
if(pos.first != pos.second){
while(pos.first != pos.second){
cout << pos.first->second << " ";
++pos.first;
}
cout << endl;
cout << "the entry will be deleted!" << endl;
pair<multi_iter, multi_iter> pos = links.equal_range(search_author);
links.erase(pos.first, pos.second);
if(!links.count(search_author))
cout << "delete successfully!" << endl;
else
cout << "error!" << endl;
}
else
cout << "There is no entry!" << endl;
//order the entries by first order
//use map to store the author name and count the entry numbers
// map order the iterm by character automatically
map<string, int> author_cnt;
//get author name from multimap and cout
for(multi_iter it = links.begin(); it != links.end(); ++it)
++author_cnt[it->first];
//put put authors
cout << "the authors and their entry numbers:" << endl;
for(map<string, int>::iterator it = author_cnt.begin(); it != author_cnt.end(); ++it)
cout << it->first << " " << it->second << endl;
cout << "order authors by first character:" << endl;
for(char c = 'A'; c != 'Z' + 1; ++c){
cout << "Authors' names begin with '" << c << "': " << endl;
for(map<string, int>::iterator it = author_cnt.begin(); it != author_cnt.end(); ++it){
string au = it->first;
if(*au.begin() == c){
cout << au << " : ";
multi_iter iter = links.find(au);
for(sz_type cnt = 0; cnt != it->second; ++cnt, ++iter)
cout << iter->second << " ";
cout << endl;
}
}
}
}