引言:
map 和 set 容器中,一个键只能对应一个实例,而 multiset 和 multimap 类型则允许一个键对应多个实例。
multimap 与 multiset 类型与相应的单元素版本具有相同的头文件定义:分别是map 与 set 头文件。
multimap/multiset 类型与 map/set 的操作相同,只有一个例外: multimap 不支持下标运算。 因为在这类容器中,某个键可能对应多个值。 因此,在使用multimap/multiset 时,对于某个键,必须做好处理多个值的准备,而非只有单一的值。
一、元素的添加与删除
前面所描述的 insert 操作和 erase 操作同样也适用于 multimap 以及 multiset 容器。只是不同的是:由于键不要求是唯一的 , 因此每次调用 insert 总会添加一个元素。
multimap<string,string> authors; authors.insert(make_pair("Barth John","Sot-Weed Factor")); authors.insert(make_pair("Barth John","Sot-Weed Factor"));
带有一个键参数的 erase 版本将 删除拥有该键的所有元素 , 并返回删除的个数。而带有一个或一对迭代器参数的版本只删除指定的元素,并返回 void 类型:
multimap<string,string> authors; authors.insert(make_pair("Barth John","Sot-Weed Factor")); authors.insert(make_pair("Barth John","Sot-Weed Factor")); authors.insert(make_pair("Barth John","Sot-Weed Factor")); string searchStr("Barth John"); multimap<string,string>::size_type cnt = authors.erase(searchStr); cout << cnt << endl; //cnt == 3
二、在 multimap 和 multiset 中查找元素
关联容器 nap 和 set 的元素是按照顺序存储的,而 multimap 和 multiset 也一样。因此,在 multiset 和 multim 容器中, 如果某个键对应于多个实例,则这些实例在容器中必然相邻存放 。
因此,在遍历 multimap/multiset 容器时,可以保证依次返回特定键所关联的所有元素。
1 、使用 find 和 count 操作
使用 count 函数可以求出某个键出现的 次数 ,而 find 操作将返回一个迭代器, 指向第一个拥有正在查找的键的实例 :
multimap<string,string> authors; string first,second; while (getline(cin,first)) { getline(cin,second); authors.insert(make_pair(first,second)); } string search_item("Alain de Botton"); typedef multimap<string,string>::size_type sz_type; sz_type entries = authors.count(search_item); multimap<string,string>::iterator iter = authors.find(search_item); for (sz_type cnt = 0;cnt != entries; ++cnt,++iter) { cout << iter -> second << endl; } cout << "First:\t\t\tSecond:" << endl; for (multimap<string,string>::iterator iter = authors.begin(); iter != authors.end(); ++iter) { cout << iter -> first << "\t\t" << iter -> second << endl; }
2 、与众不同的面向迭代器的解决方案
下表列出的这些操作适用于所有的关联容器,也可用于普通的 map 和 set 容器,但是更常用于 multiamp 和 multiset 容器。所有的这些操作都需要一个键,并返回一个迭代器。
返回迭代器的关联容器操作 |
|
---|---|
m.lower_bound(k) |
返回一个迭代器,指向键 不小于 k 的第一个元素 |
m.upper_bound(k) |
返回一个迭代器,指向键 大于 k 的第一个元素 |
m.equal_range(k) |
他的 first 成员等价于 m.lower_bound(k), second 成员等价于 m.upper_bound(k) |
在同一个键上调用 lower_bound 函数和 upper_bound 函数将产生一个迭代器范围,指出该键所关联的所有元素。如果该键在容器中存在 , 则会获得两个不同的迭代器:lower_bound 返回的迭代器指向该键关联的第一个实例 , 而 upper_bound 返回的迭代器则指向最后一个实例的下一位置。如果该键不在 multimap 中 , 这两个操作将 返回同一个迭代器 , 都指向 同一个元素或者同时指向 multimap 超出末端的位置 。
typedef multimap<string,string>::iterator authors_it; string searchVal = "Barth John"; authors_it beg = authors.lower_bound(searchVal), end = authors.upper_bound(searchVal); while (beg != end) { cout << beg -> second << endl; ++ beg; }
这两个操作不会说明键的存在,其关键之处在于返回值给出了迭代器范围。
3 、 equal_range 函数
可用通过调用 equal_range 函数来取代调用 upper_bound 和 lower_bound 函数。equal_range 函数返回存储一对迭代器的 pair 对象。如果该值存在 , 则 pair 对象中的第一个迭代器指向该键关联的第一个实例 , 第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素 , 则 pair 对象中的两个迭代器都将指向此键应该插入的位置。
pair<authors_it,authors_it> pos = authors.equal_range(searchVal); while (pos.first != pos.second) { cout << pos.first -> second << endl; ++ pos.first; }
//附:以上两个程序的未用typedef版本 string searchVal = "Barth John"; multimap<string,string>::iterator beg = authors.lower_bound(searchVal), end = authors.upper_bound(searchVal); cout << "First:\t\t\tSecond:" << endl; while (beg != end) { cout << beg -> first << "\t\t" << beg -> second << endl; ++ beg; } cout << endl; pair<multimap<string,string>::iterator,multimap<string,string>::iterator> pos = authors.equal_range(searchVal); while (pos.first != pos.second) { cout << pos.first -> first << "\t\t" << pos.first -> second << endl; ++ pos.first; }
//P325 习题10.26 int main() { multimap<string,string> authors; ifstream inFile("input"); string first,second; while (getline(inFile,first)) { getline(inFile,second); authors.insert(make_pair(first,second)); } string eraseVal("Barth John"); if (authors.find(eraseVal) != authors.end()) { multimap<string,string>::size_type cnt = authors.erase(eraseVal); cout << "Have erased " << cnt << " books" << endl; } else { cout << "Have erased 0 book" << endl; } }
//习题10.27 multimap<string,string> authors; ifstream inFile("input"); string first,second; while (getline(inFile,first)) { getline(inFile,second); authors.insert(make_pair(first,second)); } string eraseVal("Barth John"); pair<multimap<string,string>::iterator,multimap<string,string>::iterator> pos = authors.equal_range(eraseVal); if (pos.first != pos.second) { authors.erase(pos.first,pos.second); } else { cout << "Have not found eraseVal " << eraseVal << endl; } cout << "First:\t\tSecond:" << endl; for (multimap<string,string>::iterator iter = authors.begin(); iter != authors.end(); ++iter) { cout << iter -> first << "\t" << iter -> second << endl; }
//习题10.28 这节中非常好的习题,值得仔细品读 int main() { multimap<string,string> authors; string author,book; ifstream inFile("input"); while (getline(inFile,author)) { getline(inFile,book); authors.insert(make_pair(author,book)); } multimap<string,string>::iterator iter = authors.begin(); if (iter == authors.end()) { cout << "map is empty" << endl; return 0; } /* for (multimap<string,string>::iterator it = authors.begin(); it != authors.end(); ++it) { cout << it -> first << ' ' << it -> second << endl; } */ string currAuthor,preAuthor; do { currAuthor = iter -> first; if (preAuthor.empty() || currAuthor[0] != preAuthor[0]) { cout << "Author Names Beginning with " << currAuthor[0] << " :" << endl; } cout << "\t" << currAuthor; pair<multimap<string,string>::iterator,multimap<string,string>::iterator> pos = authors.equal_range(currAuthor); while (pos.first != pos.second) { cout << ',' << pos.first -> second; ++ (pos.first); } cout << endl; iter = pos.second; preAuthor = currAuthor; } while (iter != authors.end()); }