【C++】multi容器-multimap

(点击上方公众号,可快速关注)

C++标准库提供了很多实用的数据结构,能大大地简化开发中重新造轮子的成本。但实际开发中通常仅用到一些常用的类,加之很多C++的书籍也仅介绍一些高频的类,导致一些特殊应用的类“鲜有人闻”。multi-容器就是一类容易被忽视的数据结构,本篇介绍的是multimap容器类。

在进行这篇文章前,先说一点开发中常见的一类现象。看过和做过很多的项目,有自己的,也有别人的,发现很多代码在选用数据结构时并不准确,这很容易导致代码意图不能准确地传达给阅读者。最常见的实例是:不管存什么东西都用顺序存储结构,如,用vector存储本该使用map存储的键值对,用vector存储本该使用multimap存储的多重键值对。虽然可以工作,但带来的出错的概率和维护的成本是很大的。我们应该养成不同的情况选用适合的数据结构的习惯,C++标准库提供了相对丰富的数据结构,我们何乐而不为呢?

multimap引入

map类似于数学中的函数,一个输入只能得到唯一的输出;在C++实现为模板类map,应用于一个key只能对应一个value的情况。如,通过身份证号查询姓名的需求,存储结构可以设计为:

using IDCard = std::string;
using RealName= std::string;
std::multimap<IDCard,RealName> mapCard2Name;

由于身份证号的唯一性,我们可以通过它得到唯一的真实姓名,上面的设计完全满足需求。如果,还要求通过姓名查询关联的身份证,map类就不能简单地满足了,因为同名的人是普遍存在的。当然解决的方法很多,可以通过顺序结构存储,然后排序,利用相同名字的元素挨在一起的性质将满足名字条件的元素提取出来;也可以使用map存储,只不过值是一个vector类型,每次插入和删除都要自己维护vector里的元素。这两种方法都有额外的开发成本。

multimap能原生支持上述应用,跟数学中的关系类似。multimap api与map大同小异,下面分几个小节介绍,重点介绍特有的一些操作接口。

multimap接口

初始化操作

常用的是默认构造函数,然后使用insert成员函数地将关系动态地插入。

using IDCard = std::string;
using RealName= std::string;
std::multimap<IDCard,RealName> mapCard2Name;
mapCard2Name.insert(make_pair("张三","xx1"));
mapCard2Name.insert(make_pair("张三","xx2"));
mapCard2Name.insert(make_pair("李四","xx3"));


也可以使用C++11引入的初始化列表方便地构造对象:

using IDCard = std::string;
using RealName= std::string;
std::multimap<IDCard,RealName> mapCard2Name = {{"张三","xx1"},{"张三","xx2"},{"李四","xx3"}};

但这只适用于关系已知的情况。

其他构造函数可以查阅API 文档。

查询操作

multimap也有sizeempty函数,还拿上面的例子:

mapCard2Name.size();  // 返回3
mapCard2Name.empty(); // false

修改操作

常用操作inserteraseclear,这三个函数在STL容器类中具有相同的语义。

insert用来插入关系,clear会清空所有关系,erase可有选择的清除关系,在此不赘述。

遍历

这个操作是重点:假设我们要遍历出所有姓名为“张三”的人的身份证号,该怎么做呢?

大体有三种方式:

(1)结合使用findcount

mltimap在实现层面会将同个key的多个元素存储在一起,所以使用find函数通常返回第一个元素的地址;count会返回该key的元素的个数,结合使用即可实现遍历:

auto it = mapCard2Name.find("张三");
for(int k = 0;k != mapCard2Name.count("张三");k++,it++)
   cout<<it->first<<"--"<<it->second<<endl;

特别注意:上文加黑的通常,虽然大部分实现的行为如此,但在cppreference.com中的描述并没有明确说明这一点:

Finds an element with key equivalent to key. If there are several elements with key in the container, any of them may be returned.

所以不推荐这种方式。

(2)结合使用lower_boundupper_bound

lower_bound会返回不小于给定key的首个元素的地址;而upper_bound则返回大于给定key的首个元素的地址。这跟普通的迭代器的范围是一致的:

auto beg = mapCard2Name.lower_bound("张三");
auto end = mapCard2Name.upper_bound("张三");
for(auto it = beg; it != end; it++)
   cout<<it->first<<"--"<<it->second<<endl;

(3)使用equal_range函数

这比方式二更简单。方式二需要使用两个函数得到给定key的元素的范围,方式三一个函数就可以得到,它的返回值一个std::pair,第一个元素迭代器的开始地址,第二个元素是迭代器的结束地址。

auto beg = mapCard2Name.equal_range("张三").first;
auto end = mapCard2Name.equal_range("张三").second;
for(auto it = beg; it != end; it++)
   cout<< it->first<<"--"<< it->second<<endl;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值