multimap 和 multiset 类型

map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap类型则允许一个键对应多个实例。例如,在电话簿中,每个人可能有单独的电话号码列表。在作者的文章集中,每位作者可能有单独的文章标题列表。multimap和 multiset 类型与相应的单元素版本具有相同的头文件定义:分别是 map 和set 头文件。

multimap 和 multiset 所支持的操作分别与 map 和 set 的操作相同,只有一个例外:multimap 不支持下标运算。不能对 multimap 对象使用下标操作,因为在这类容器中,某个键可能对应多个值。为了顺应一个键可以对应多个值这一性质,map 和 multimap,或 set 和 multiset 中相同的操作都以不同的方式做出了一定的修改。在使用 multimap 或 multiset 时,对于某个键,必须做好处理多个值的准备,而非只有单一的值。

元素的添加和删除

由于键不要求是唯一的,因此每次调用 insert 总会添加一个元素。例如,可如下定义一个 multimap 容器对象将作者映射到他们所写的书的书名上。这样的映射可为一个作者存储多个条目:

// adds first element with key Barth
authors.insert(make_pair(
string("Barth, John"),
string("Sot-Weed Factor")));
// ok: adds second element with key Barth
authors.insert(make_pair(
string("Barth, John"),
string("Lost in the Funhouse")));

带有一个键参数的 erase 版本将删除拥有该键的所有元素,并返回删除元素的个数。而带有一个或一对迭代器参数的版本只删除指定的元素, 并返回 void类型:

multimap<string, string> authors;
string search_item("Kazuo Ishiguro");
// erase all elements with this key; returns number of elements removed
multimap<string, string>::size_type cnt =authors.erase(search_item);

在 multimap 和 multiset 中查找元素

注意到,关联容器 map 和 set 的元素是按顺序存储的。而 multimap 和multset 也一样。因此,在 multimap 和 multiset 容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。

迭代遍历 multimap 或 multiset 容器时,可保证依次返回特定键所关联的所有元素。

在 map 或 set 容器中查找一个元素很简单——该元素要么在要么不在容器中。但对于 multimap 或 multiset,该过程就复杂多了:某键对应的元素可能出现多次。例如,假设有作者与书名的映射,我们可能希望找到并输出某个作者写的所有书的书名。

事实证明,上述问题可用三种策略解决。而且三种策略都基于一个事实——在 multimap 中,同一个键所关联的元素必然相邻存放。

使用 find 和 count 操作

count 函数求出某键出现的次数, 而 find 操作则返回一个迭代器, 指向第一个拥有正在查找的键的实例:

// author we'll look for
string search_item("Alain de Botton");
// how many entries are there for this author
typedef multimap<string, string>::size_type sz_type;
sz_type entries = authors.count(search_item);
// get iterator to the first entry for this author
multimap<string,string>::iterator iter =
authors.find(search_item);
// loop through the number of entries there are for this author
for (sz_type cnt = 0; cnt != entries; ++cnt, ++iter) cout <<
iter->second << endl; // print each title

首先,调用 count 确定某作者所写的书籍数目,然后调用 find 获得指向第一个该键所关联的元素的迭代器。for 循环迭代的次数依赖于 count 返回的值。在特殊情况下,如果 count 返回 0 值,则该循环永不执行。

与众不同的面向迭代器的解决方案

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)

这些操作适用于所有的关联容器,也可用于普通的 map 和 set 容器,但更常用于 multimap 和 multiset。所有这些操作都需要传递一个键,并返回一个迭代器。在同一个键上调用 lower_bound 和 upper_bound,将产生一个迭代器范围,指示出该键所关联的所有元素。如果该键在容器中存在,则会获得两个不同的迭代器: lower_bound 返回的迭代器指向该键关联的第一个实例,而 upper_bound 返回的迭代器则指向最后一个实例的下一位置。如果该键不在 multimap 中,这两个操作将返回同一个迭代器,指向依据元素的排列顺序该键应该插入的位置。

当然,这些操作返回的也可能是容器自身的超出末端迭代器。如果所查找的元素拥有 multimap 容器中最大的键,那么的该键上调用 upper_bound 将返回超出末端迭代器。如果所查找的键不存在,而且比 multimap 容器中所有的键都大,则 low_bound 也将返回超出末端迭代器。

lower_bound 返回的迭代器不一定指向拥有特定键的元素。如果该键不在容器中,则 lower_bound 返回在保持容器元素顺序的前提下该键应被插入的第一个位置。

使用这些操作,可如下重写程序:

// definitions of authors and search_item as above
// beg and end denote range of elements for this author
typedef multimap<string, string>::iterator authors_it;
authors_it beg = authors.lower_bound(search_item),
end = authors.upper_bound(search_item);
// loop through the number of entries there are for this author
while (beg != end) {
cout << beg->second << endl; // print each title
++beg;
}

这个程序实现的功能与前面使用 count 和 find 的程序相同,但任务的实现更直接。调用 lower_bound 定位 beg 迭代器,如果键 search_item 在容器中存在,则使 beg 指向第一个与之匹配的元素。如果容器中没有这样的元素,么 beg 将指向第一个键比 search_item 大的元素。调用 upper_bound 设置end 迭代器,使之指向拥有该键的最后一个元素的下一位置。这两个操作不会说明键是否存在,其关键之处在于返回值给出了迭代器范围。

若该键没有关联的元素,则 lower_bound 和 upper_bound 返回相同的迭代器:都指向同一个元素或同时指向 multimap 的超出末端位置。它们都指向在保持容器元素顺序的前提下该键应被插入的位置。

如果该键所关联的元素存在,那么 beg 将指向满足条件的元素中的第一个。可对 beg 做自增运算遍历拥有该键的所有元素。当迭代器累加至 end 标志时,表示已遍历了所有这些元素。当 beg 等于 end 时,表示已访问所有与该键关联的元素。

假设这些迭代器标记某个范围,可使用同样的 while 循环遍历该范围。该循环执行 0 次或多次,输出指定作者所写的所有书的书名(如果有的话)。如果没有相关的元素, 那么 beg 和 end 相等, 循环永不执行。 否则, 不断累加 beg

将最终到达 end,在这个过程中可输出该作者所关联的记录。

enual_range 函数

事实上,解决上述问题更直接的方法是:调用 equal_range 函数来取代调用 upper_bound 和 lower_bound 函数。equal_range 函数返回存储一对迭代器的 pair 对象。如果该值存在,则 pair 对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素,则 pair 对象中的两个迭代器都将指向此键应该插入的位置。

使用 equal_range 函数再次修改程序:

// definitions of authors and search_item as above
// pos holds iterators that denote range of elements for this key
pair<authors_it, authors_it>
pos = authors.equal_range(search_item);
// loop through the number of entries there are for this author
while (pos.first != pos.second) {
cout << pos.first->second << endl; // print each title
++pos.first;
}

这个程序段与前面使用 upper_bound 和 lower_bound 的程序基本上是相同的。本程序不用局部变量 beg 和 end 来记录迭代器范围,而是直接使用equal_range 返回的 pair 对象。该 pair 对象的 first 成员存储

lower_bound 函数返回的迭代器,而 second 成员则记录 upper_bound 函数返回的迭代器。

因此,本程序的 pos.first 等价于前一方法中的 beg,而 pos.second 等价于end。

posted @ 2018-05-07 23:14  刘-皇叔  阅读(759)  评论(0)    收藏  举报