map 和 set 容器中,一个键只能对应一个实例。
而 multiset 和 multimap 类型则允许一个键对应多个实例。
例如,在电话簿中,每个人可能有单独的电话号码列表;
在作者的文章集中,每位作者可能有单独的文章标题列表。

multimap/ultiset 类型的定义也在 map 和 set 头文件。
multimap/multiset 所支持的操作与 map/set 只有一个不同:
multimap 不支持下标运算。因为在这类容器中,某个键可能对应多个值。
因此,multiset/multimap 对应 set/map 中相同的操作都以不同的方式做出了一定的修改。

[1. 元素的添加和删除]

insert 操作和 erase 操作同样适用于 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);

[2. 在 multimap 和 multiset 中查找元素]

注意到,关联容器 map/set 的元素是按顺序存储的。而 multimap/multset 也一样。
因此,在 multimap 和 multiset 容器中,
如果某个键对应多个实例,则这些实例在容器中将相邻存放。
迭代遍历 multimap 或 multiset 容器时,可保证依次返回特定键所关联的所有元素。

在 map 或 set 容器中查找一个元素很简单——该元素要么在要么不在容器中。
但对于 multimap 或 multiset,该过程就复杂多了:某键对应的元素可能出现多次。
例如,假设有作者与书名的映射,我们可能希望找到并输出某个作者写的所有书的书名。
事实证明,上述问题可用 3 种策略解决。
而且 3 种策略都基于一个事实——在 multimap 中,同一个键所关联的元素必然相邻存放。

首先介绍第 1 种策略:使用 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 = ; cnt != entries; ++cnt, ++iter)
  cout << iter->second << endl; // print each title

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

第 2 种方法,调用 lower_bound 和 upper_bound 函数。

表 10.8 列出的这些操作适用于所有的关联容器,
也可用于普通的 map 和 set 容器,但更常用于 multimap 和 multiset。
所有这些操作都需要传递一个键,并返回一个迭代器。

// 表 10.8. 返回迭代器的关联容器操作
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 中,这两个操作将返回同一个迭代器,指向依据元素的排列顺序该键应该插入的位置。

当然,这些操作返回的也可能是容器自身的超出末端迭代器。
如果所查找的元素拥有 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;
}

调用 lower_bound 定位 beg 迭代器,
如果键 search_item 在容器中存在,则使 beg 指向第一个与之匹配的元素。
如果容器中没有这样的元素,那么 beg 将指向第一个键比 search_item 大的元素。
调用 upper_bound 设置 end 迭代器,使之指向拥有该键的最后一个元素的下一位置。
这两个操作不会说明键是否存在,其关键之处在于返回值给出了迭代器范围。

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

若该键所关联的元素存在,那么 beg 将指向满足条件的元素中的第一个。
当 beg 等于 end 时,表示已访问所有与该键关联的元素。
该循环执行 0 次或多次,输出指定作者所写的所有书的书名(如果有的话)。
如果没有相关的元素,那么 beg 和 end 相等,循环永不执行。
否则,不断累加 beg 将最终到达 end,在这个过程中可输出该作者所关联的记录。

第 3 种方法,调用 equal_range 函数是更直接的方法。
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。

multimap 和 multiset 类型的更多相关文章

  1. 10.1——pair,map,set,multimap,multiset

    map和set只允许相同的键出现一次,而multimap和multiset则允许出现多次. 1. 引言——pair类型: pair需要添加头文件utility头文件 make_pair<v1,v ...

  2. multimap和multiset 认知和使用

    之前只是在C++ Primer里面看过关联容器,可能因为没有实际用过,只是看看,所以导致用的时候并不熟悉: 在这之前,map和set的特性应该要了解,map是关联数组,也就是由键值对组成的,而set只 ...

  3. 非关联容器|hash|unordered_map/multimap,unordered_set/multiset

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  4. guava中Multimap、Multiset使用

    guava中的Multimap接口 Multimap和java.util.Map接口没有任何继承关系.同Map一样,也是放键值对,但是Multimap的值是一个集合.同样支持泛型,假如键值对的key的 ...

  5. C++ 关联容器

    <C++ Primer 4th>读书笔记 关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素. 关联容器(Ass ...

  6. c++ primer 10 关联容器

    关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,顺序容器则通过元素在容器中的位置顺序存储和访问元素 关联容器类型 map 关联数组:元素通过键来存储和读取 set 大小可变的 ...

  7. C++STL——概述

    一.相关介绍 STL 标准模板库 在编写代码的过程中有一些程序经常会被用到,而且需求特别稳定,所以C++中把这些常用的模板做了统一的规范,慢慢的就形成了STL 提供三种类型的组件: 容器.迭代器和算法 ...

  8. C++拾遗(七)——关联容器

    关联容器(Associative containers)支持通过键来高效地查找和读取元素.两个基本的关联容器类型是 map 和set.map 的元素以键-值(key-value)对的形式组织:键用作元 ...

  9. C++容器(二):关联容器简介

    关联容器(associative container)与顺序容器的本质区别在于:关联容器通过键(Key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素.虽然,关联容器的大部分行为 ...

随机推荐

  1. asp.net中TextBox里面实现回车触发指定事件

    原文:asp.net中TextBox里面实现回车触发指定事件 我在一个user_top用户控件里面做了个包括搜索的功能.然后再一个页面中添加这个用户控件.浏览时候在textbox里面输入搜索内容后.下 ...

  2. Jquery AJAX POST与GET之间的区别

    1:GET访问 浏览器 认为 是等幂的就是 一个相同的URL 只有一个结果[相同是指 整个URL字符串完全匹配]所以 第二次访问的时候 如果 URL字符串没变化 浏览器是 直接拿出了第一次访问的结果 ...

  3. 快速构建Windows 8风格应用28-临时应用数据

    原文:快速构建Windows 8风格应用28-临时应用数据 本篇博文主要介绍临时应用数据概览.如何构建临时应用数据. 一.临时应用数据概览 临时应用数据相当于网页中缓存,这些数据文件是不能够漫游的,并 ...

  4. 机器学习学习-Types of learning

    Types of learning 基于个人理解.于我们在面对一个详细的问题时.可以依据要达到的目标选择合适的机器学习算法来得到想要的结果.比方,推断一封电子邮件是否是垃圾邮件,就要使用分类(clas ...

  5. android JBOX2D粒子碰撞的实例,以达到特殊效果

    最近完成动画特效工作的一个发展.的效果,所以传统的三大动画无法满足咱们的需求啦(事实上这不是一个动画效果的议题.事实上有一点点游戏的感觉). 寻找一个粒子系统吧,发现JBox2D比較简单的能满足咱们 ...

  6. 你是否应该使用一个Javascript MVC框架?

    你是否应该使用一个Javascript MVC框架?本文摘自smashingmagazine的Journey Through The JavaScript MVC Jungle部分内容,希望对大家有帮 ...

  7. 关于如何惟一地标识一台Android设备的综合性讨论

    想必大家在开发Android项目的时候,多多少少会遇到“如何惟一地标识一台Android设备”等类似的问题.不只是以前,即使是现在乃至可以预见的将来,这个问题都将一直存在. 如果大家使用搜索工具搜索的 ...

  8. 用C++进行简单的文件I/O操作-转自VC知识库

    原文请见 http://www.vckbase.com/index.php/wv/1158 序论 我曾发表过文件输入输出的文章,现在觉得有必要再写一点.文件 I/O 在C++中比烤蛋糕简单多了. 在这 ...

  9. 转载Mvc的多层架构

    Mvc的多层架构 分享一个Mvc的多层架构,欢迎大家拍砖斧正   多层架构是什么? 多层架构是开发人员在开发过程当中面对复杂且易变的需求采取的一种以隔离控制为主的应对策略,关于多层架构的标准,我认为有 ...

  10. 解决C# WinForm 中 VSHOST.EXE 程序不关闭的问题

    右击“解决方案”--属性-调试栏-启用调试器部分-“启用Visual studio宿主进程”不勾选