在“使用 <map> 库创建关联容器”一文中,我们讨论了标准库中的 map 关联容器。但那只是 map 容器的一部分。标准库还定义了一个 multimap 容器,它与 map 类似,所不同的是它允许重复键。这个属性使得 multimap 比预想的要更有用:比如在电话簿中相同的人可以有两个以上电话号码,文件系统中可以将多个符号链接映射到相同的物理文件,或DNS服务器可以将几个URLs映射到相同的IP地址。在这些场合,你可以象下面这样:

1.// 注: 伪码
2.multimap <string, string> phonebook;
3.phonebook.insert("Harry","8225687"); // 家里电话
4.phonebook.insert("Harry","555123123"); // 单位电话
5.phonebook.insert("Harry"," 2532532532"); // 移动电话

在 multimap 中能存储重复键的能力大大地影响它的接口和使用。那么如何创建非唯一键的关联容器呢?答案是使用在 <map> 库中定义的 multimap 容器。

提出问题

与 map 不同,multimap 可以包含重复键。这就带来一个问题:重载下标操作符如何返回相同键的多个关联值?以下面的伪码为例:

1.string phone=phonebook["Harry];

标准库设计者的解决这个问题方法是从 multimap 中去掉下标操作符。因此,需要用不同的方法来插入和获取元素以及和进行错误处理。

插入

假设你需要开发一个 DNS 后台程序(也就是 Windows 系统中的服务程序),该程序将 IP 地址映射匹配的 URL 串。你知道在某些情况下,相同的 IP 地址要被关联到多个 URLs。这些 URLs 全都指向相同的站点。在这种情况下,你应该使用 multimap,而不是 map。例如:

1.#include <map>
2.#include <string>
3. 
4.multimap <string, string> DNS_daemon;

用 insert() 成员函数而不是下标操作符来插入元素。insert()有一个 pair 类型的参数。在“使用 <map> 库创建关联容器”中我们示范了如何使用 make_pair() 辅助函数来完成此任务。你也可以象下面这样使用它:

1.DNS_daemon.insert(make_pair("213.108.96.7","cppzone.com"));

在上面的 insert()调用中,串 “213.108.96.7”是键,“cppzone.com”是其关联的值。以后插入的是相同的键,不同的关联值:

1.DNS_daemon.insert(make_pair("213.108.96.7","cppluspluszone.com"));

因此,DNS_daemon 包含两个用相同键值的元素。注意 multimap::insert() 和 map::insert() 返回的值是不同的。

1.typedef pair <const Key, T> value_type;
2.iterator
3.insert(const value_type&); // #1 multimap
4. 
5.pair <iterator, bool>
6.insert(const value_type&); // #2 map

multimap::insert()成员函数返回指向新插入元素的迭代指针,也就是 iterator(multimap::insert()总是能执行成功)。但是 map::insert() 返回 pair<iterator, bool>,此处 bool 值表示插入操作是否成功。

查找单个值

与 map 类似,multimap 具备两个版本重载的 find()成员函数:

1.iterator find(const key_type& k);
2.const_iterator find(const key_type& k) const;

find(k) 返回指向第一个与键 k 匹配的 pair 的迭代指针,这就是说,当你想要检查是否存在至少一个与该键关联的值时,或者只需第一个匹配时,这个函数最有用。例如:

01.typedef multimap <string, string> mmss;
02.void func(const mmss & dns)
03.{
04.mmss::const_iterator cit=dns.find("213.108.96.7");
05.if (cit != dns.end())
06.cout <<"213.108.96.7 found" <<endl;
07.else
08.cout <<"not found" <<endl;
09.}

处理多个关联值

count(k) 成员函数返回与给定键关联的值得数量。下面的例子报告了有多少个与键 “213.108.96.7” 关联的值:

1.cout<<dns.count("213.108.96.7"//output: 2
2.<<" elements associated"<<endl;

为了存取 multimap 中的多个值,使用 equal_range()、lower_bound()和 upper_bound()成员函数:

equal_range(k):该函数查找所有与 k 关联的值。返回迭代指针的 pair,它标记开始和结束范围。下面的例子显示所有与键“213.108.96.7”关联的值:

1.typedef multimap <string, string>::const_iterator CIT;
2.typedef pair<CIT, CIT> Range;
3.Range range=dns.equal_range("213.108.96.7");
4.for(CIT i=range.first; i!=range.second; ++i)
5.cout << i->second << endl; //output: cpluspluszone.com
6.// cppzone.com

lower_bound() 和 upper_bound():lower_bound(k) 查找第一个与键 k 关联的值,而 upper_bound(k) 是查找第一个键值比 k 大的元素。下面的例子示范用 upper_bound()来定位第一个其键值大于“213.108.96.7”的元素。通常,当键是一个字符串时,会有一个词典编纂比较:

1.dns.insert(make_pair("219.108.96.70""pythonzone.com"));
2.CIT cit=dns.upper_bound("213.108.96.7");
3.if (cit!=dns.end()) //found anything?
4.cout<<cit->second<<endl; //display: pythonzone.com

如果你想显示其后所有的值,可以用下面这样的循环:

01.// 插入有相同键的多个值
02.dns.insert(make_pair("219.108.96.70","pythonzone.com"));
03.dns.insert(make_pair("219.108.96.70","python-zone.com"));
04. 
05.// 获得第一个值的迭代指针
06.CIT cit=dns.upper_bound("213.108.96.7");
07. 
08.// 输出: pythonzone.com,python-zone.com
09.while(cit!=dns.end())
10.{
11.   cout<<cit->second<<endl;
12.   ++cit;
13.}

结论

虽然 map 和 multimap 具有相同的接口,其重要差别在于重复键,设计和使用要区别对待。此外,还要注意每个容器里 insert()成员函数的细微差别。

使用multimap创建重复键关联容器的更多相关文章

  1. [C++ Primer] : 第11章: 关联容器

    目录 使用关联容器 关联容器概述 关联容器操作 无序容器 使用关联容器 关联容器与顺序容器有着根本的不同: 关联容器中的元素是按关键字来保存和访问的, 按顺序容器中的元素是按它们在容器中的位置来顺序保 ...

  2. STL之关联容器

    关联容器包含map.set.multimap.multiset. 关联容器的特点是明显的,相对于顺序容器,有如下特点: 1.其内部是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的. 2.se ...

  3. 《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-13  过滤预先加载的实体集合 问题 你想过滤预先加载的实体集合,另外,你想使用 ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (37) ------ 第六章 继承与建模高级应用之独立关联与外键关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-13  在基类中应用条件 问题 你想从一个已存在的模型中的实体派生一个新的实体, ...

  5. orm 语法 数据库连接、建表、增删改查、回滚、单键关联 、多键关联、三表关联

    1.数据库连接, #!usr/bin/env/python # -*- coding:utf-8 -*- # from wangteng import sqlalchemy from sqlalche ...

  6. Python sqlalchemy orm 外键关联

    创建外键关联 并通过relationship 互相调用 如图: 实现代码: import sqlalchemy # 调用链接数据库 from sqlalchemy import create_engi ...

  7. Python sqlalchemy orm 多对多外键关联

    多对多外键关联 注:使用三张表进行对应关联 实现代码: # 创建3个表 配置外键关联 # 调用Column创建字段 加类型 from sqlalchemy import Table, Column, ...

  8. STL的基本使用之关联容器:map和multiMap的基本使用

    STL的基本使用之关联容器:map和multiMap的基本使用 简介 map 和 multimap 内部也都是使用红黑树来实现,他们存储的是键值对,并且会自动将元素的key进行排序.两者不同在于map ...

  9. 09--STL关联容器(map/multimap)

    一:map/multimap的简介 map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供基于key的快速检索能力. map中key值是唯一的.集合中的元素按一定的顺 ...

随机推荐

  1. Science:给青年科研工作者的忠告

  2. 讨论下IDS的绕过

    自从知道dedecms自带了80sec的内置Mysqlids后,一直以来也没有想到绕过的办法.或者是自己mysql的根底太差了吧.于是分析dedecms源码时,只找模板执行,本地包含,上传等,完全没有 ...

  3. Linux常用命令大全(转载)

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  4. asp.net 创建文字特效

    相信word 中的 艺术字 功能大家都不陌生.今天, 我们就利用C#来制作几款自己的艺术字, 可能会对我们了解字体图像的制作原理有一些帮助. 至于有没有使用价值我保持沉默. 一. 投影效果 程序运行效 ...

  5. android 关于Location of the Android SDK has not been setup in the preferences的解决方法

    今天在部署android开发环境的时候,每次打开eclipse的时候点击AVD Manager的按钮就会弹出Location of the Android SDK has not been setup ...

  6. Appstore提交 被拒绝

    Reasons 16.1: Apps that present excessively objectionable or crude content will be rejected 16.1 We ...

  7. logback日志配置文件代码示例

    <?xml version="1.0" encoding="UTF-8"?> <configuration scan="true&q ...

  8. MYSQL注入天书之order by后的injection

    Background-9  order by后的injection 此处应介绍order by后的注入以及limit注入,我们结合less-46更容易讲解,(在less46中详细讲解)所以此处可根据l ...

  9. 【Apache运维基础(4)】Apache的Rewrite攻略(1)

    简述 Rewirte主要的功能就是实现URL的跳转,它的正则表达式是基于Perl语言.可基于服务器级的(httpd.conf)和目录级的 (.htaccess)两种方式.如果要想用到rewrite模块 ...

  10. php with bootstrap

    最近用ThinkPHP+bootstrap3写的后台 用bootstrap结合php搭建MIS系统框架 Free Bootstrap Themes & Templates pgoa盘古OA开发 ...