博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Map容器
阅读量:4328 次
发布时间:2019-06-06

本文共 10974 字,大约阅读时间需要 36 分钟。

1、Map

(1)定义 

Map是标准关联式容器(associative container)之一,一个map是一个键值对序列,即(key ,value)对。它提供基于key的快速检索能力,在一个map中key值是唯一的。map提供双向迭代器,即有从前往后的(iterator),也有从后往前的(reverse_iterator)。

Map要求能对key进行<操作,且保持按key值递增有序,因此map上的迭代器也是递增有序的。如果对于元素并不需要保持有序,可以使用hash_map

Map中key值是唯一的,如果已存在一个键值对(name,code):("name_a",1),而我们还想插入一个键值对("name_a",1)则会报错(不是报错,准确的说是,返回插入不成功!)。而我们又的确想这样做,即一个键对应多个值,multimap可实现这个功能

(2)底层实现

Map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。

由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,默认用的是小于号,即less<>。 

Map中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的,如果用map函数可以实现的功能,而STL  Algorithm也可以完成该功能,建议用map自带函数,效率高一些。 

Map在空间上的特性,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),这些地方很费内存。

(3)特点

Map类似于数据库中的1:1关系,它是一种关联容器,提供一对一(C++ primer中文版中将第一个译为键,每个键只能在map中出现一次,第二个被译为该键对应的值)的数据处理能力,这种特性了使得map类似于数据结构里的红黑二叉树。Multimap类似于数据库中的1:N关系,它是一种关联容器,提供一对多的数据处理能力。

(4)性能分析

a)、hash_map和map的区别在哪里?

    (1)构造函数  hash_map需要hash函数,等于函数;map只需要比较函数(小于函数)。

    (2)存储结构  hash_map采用hash表存储,map一般采用红黑树实现。因此内存数据结构是不一样的。

b)、什么时候需要使用hash_map,什么时候需要map?

总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别。

但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且 hash_map的构造速度较慢。

因此,选择的时候需要权衡三个因素: 查找速度,数据量,内存使用。

 

c)、如何用hash_map替换程序中已有的map容器?

这个很容易,但需要你有良好的编程风格。建议你尽量使用typedef来定义你的类型:

typedef map KeyMap;

当你希望使用hash_map来替换的时候,只需要修改:

typedef hash_map KeyMap;

其他的基本不变。当然,你需要注意是否有Key类型的hash函数和比较函数。

2、成员函数

(1)构造函数

map(); // 默认构造函数map(const map& m) // 拷贝构造函数map(iterator begin, iterator end ); //区间构造函数map(iterator begin, iterator end, const traits& _compare) //带比较谓词的构造函数map(iterator begin, iterator end, const traits& _compare, const allocator& all) //带分配器

经过分析发现,map的构造函数主要是调用“拷贝构造函数”和利用“迭代器”进行初始化两种方式,因为map中每个节点由一对值构成。

(2)插入操作

(1)、insert(pair
(key1,value1))//用insert函数插入pair数据(2)、insert(map
::value_type(key1,value1));//用insert函数插入value_type数据,这种插入方式和第一种基本相似(3)、利用数组进行插入

示例:

#include #include 
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent.insert(pair
(1, “student_one”)); mapStudent.insert(pair
(2, “student_two”)); mapStudent.insert(pair
(3, “student_three”)); map
::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<
first<<” ”<
second<
#include
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent.insert(map
::value_type (1, “student_one”)); mapStudent.insert(map
::value_type (2, “student_two”)); mapStudent.insert(map
::value_type (3, “student_three”)); map
::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<
first<<” ”<
second<
#include
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent[1] = “student_one”; mapStudent[2] = “student_two”; mapStudent[3] = “student_three”; map
::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<
first<<” ”<
second<

  

以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的:第一种和第二种在效果上是一样的,用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值,如下:

mapStudent.insert(map
::value_type (1, “student_one”));mapStudent.insert(map
::value_type (1, “student_two”));

上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么我们怎么知道insert语句是否插入成功,可以用pair来获得是否插入成功,程序如下:

Pair
::iterator, bool> Insert_Pair;Insert_Pair = mapStudent.insert(map
::value_type (1, “student_one”));

我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话:Insert_Pair.second应该是true的,否则为false。

下面给出完成代码,演示插入成功与否问题:

#include #include 
#include
Using namespace std;Int main(){ Map
mapStudent;Pair
::iterator, bool> Insert_Pair; Insert_Pair=mapStudent.insert(pair
(1, “student_one”)); If(Insert_Pair.second == true) { Cout<<”Insert Successfully”<
(1, “student_two”)); If(Insert_Pair.second == true) { Cout<<”Insert Successfully”<
::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<
first<<” ”<
second<

用如下程序,看下用数组插入在数据覆盖上的效果:

#include #include 
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent[1] = “student_one”; mapStudent[1] = “student_two”; mapStudent[2] = “student_three”; map
::iterator iter; for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++){ Cout<
first<<” ”<
second<

 

(3)删除操作

(1)、erase(map
::iterator iter);//删除迭代器所指的节点(2)、erase(key k);//根据键值进行删除,删除键值k所指的节点(3)、erase(map
::iteratormap iter1,
::iteratoriter2);//删除iter1和iter2之间的数据。clear();//清空map中的数据可以用clear()函数,判定map中是否有数据可以用empty()函数,它返回true则说明是空map

示例:

#pragma warning(disable:4786)#include 
#include
#include
using namespace std;int main(){ /* map
tmp; map
::const_iterator iter1,iter2; tmp.insert(pair
(54090104,"Bob")); tmp.insert(pair
(54090105,"Ben")); iter1 = tmp.begin(); iter2 = tmp.end(); */ map
studentMessage; map
::iterator iter; //向map中插入数据 studentMessage.insert(pair
(54090101,"Mike")); studentMessage.insert(pair
(54090101,"MIKE"));//重复插入 studentMessage.insert(map
::value_type(54090102,"Sam")); studentMessage.insert(map
::value_type(54090102,"SAM"));//重复插入 studentMessage[54090103] = "Jake"; studentMessage[54090103] = "JAKE";//重复插入 //为了测试删除,先插入两个数据,看插入结果主要看上面的插入方式 studentMessage[54090104] = "Bob"; studentMessage[54090105] = "Ben"; cout<<"完成插入后map中的数据:"<
first<<" "<
second<
first<<" "<
second<
first<<" "<
second<
first<<" "<
second<

  

(4)定位查找

begin();//返回指向map头部的迭代器end();//返回指向map末尾的迭代器(1)、count();//用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了(2)、find();//用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器(3)、Lower_bound();Upper_bound();//这个方法用来判定数据是否出现,是显得笨了点://Lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器)//Upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器)//例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3Equal_range();//函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字

  

示例:

#include #include 
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent.insert(pair
(1, “student_one”)); mapStudent.insert(pair
(2, “student_two”)); mapStudent.insert(pair
(3, “student_three”)); map
::iterator iter; iter = mapStudent.find(1);if(iter != mapStudent.end()){ Cout<<”Find, the value is ”<
second<
#include
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent[1] = “student_one”; mapStudent[3] = “student_three”; mapStudent[5] = “student_five”; map
::iterator iter;iter = mapStudent.lower_bound(2);{ //返回的是下界3的迭代器 Cout<
second<
second<
second<
second<
::iterator, map
::iterator> mapPair;mapPair = mapStudent.equal_range(2);if(mapPair.first == mapPair.second) { cout<<”Do not Find”<
<<”Find”<

  

(5)数据大小

max_size();//返回map容器可能包含的元素最大个数size();//返回当前map容器中的元素个数count(); //用来查找map中某个某个键值出现的次数;

  

示例:

#pragma warning (disable:4786)#include #include 
#include
using namespace std;int main(){ map
studentMessage; map
::iterator iter; studentMessage.insert(pair
(54090101,"Mike")); studentMessage.insert(pair
(54090102,"Sam")); studentMessage.insert(pair
(54090103,"Jake")); //begin获取map中的第一个元素的迭代器,并且等于rend //end获取map中的最后一个元素下一位置的迭代器,并且等于rbegin cout<<"迭代器中的元素如下:"<
first<<" "<
second<

  

(6)遍历操作

第一种:应用前向迭代器第二种:应用反相迭代器第三种:用数组方式

示例:

#include #include 
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent.insert(pair
(1, “student_one”)); mapStudent.insert(pair
(2, “student_two”)); mapStudent.insert(pair
(3, “student_three”)); map
::reverse_iterator iter; for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++){ Cout<
first<<” ”<
second<
#include
#include
Using namespace std;Int main(){ Map
mapStudent; mapStudent.insert(pair
(1, “student_one”)); mapStudent.insert(pair
(2, “student_two”)); mapStudent.insert(pair
(3, “student_three”)); int nSize = mapStudent.size()//此处有误,应该是 for(int nIndex = 1; nIndex <= nSize; nIndex++) //by rainfish for(int nIndex = 0; nIndex < nSize; nIndex++){ Cout<
<

  

(7)排序操作

第一种:小于号重载第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载

示例:

 
#include #include 
Using namespace std;Typedef struct tagStudentInfo{ Int nID; String strName;}StudentInfo, *PStudentInfo; //学生信息Int main(){ int nSize; //用学生信息映射分数 map
mapStudent; map
::iterator iter; StudentInfo studentInfo; studentInfo.nID = 1; studentInfo.strName = “student_one”; mapStudent.insert(pair
(studentInfo, 90)); studentInfo.nID = 2; studentInfo.strName = “student_two”;mapStudent.insert(pair
(studentInfo, 80));for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) cout<
first.nID<
<
first.strName<
<
second<
 
以上程序是无法编译通过的,只要重载小于号,就OK了,如下:
Typedef struct tagStudentInfo{       Int      nID;       String   strName;       Bool operator < (tagStudentInfo const& _A) const       {              //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序              If(nID < _A.nID)  return true;              If(nID == _A.nID) return strName.compare(_A.strName) < 0;              Return false;       }}StudentInfo, *PStudentInfo;  //学生信息#include #include 
Using namespace std;Typedef struct tagStudentInfo{ Int nID; String strName;}StudentInfo, *PStudentInfo; //学生信息Classs sort{ Public: Bool operator() (StudentInfo const &_A, StudentInfo const &_B) const { If(_A.nID < _B.nID) return true; If(_A.nID == _B.nID) return _A.strName.compare(_B.strName) < 0; Return false; }};Int main(){ //用学生信息映射分数 Map
mapStudent; StudentInfo studentInfo; studentInfo.nID = 1; studentInfo.strName = “student_one”; mapStudent.insert(pair
(studentInfo, 90)); studentInfo.nID = 2; studentInfo.strName = “student_two”; mapStudent.insert(pair
(studentInfo, 80));}

  

 

转载于:https://www.cnblogs.com/yedushusheng/p/5519382.html

你可能感兴趣的文章
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-4.在线教育后台数据库设计...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_2-3.热部署在Eclipse和IDE里面的使用...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-3.在线教育站点需求分析和架构设计...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_2-4.后端项目分层分包及资源文件处理...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_2-2.快速搭建SpringBoot项目,采用IDEA...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_3-5.PageHelper分页插件使用
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-6.微信扫码登录回调本地域名映射工具Ngrock...
查看>>
小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-8.用户模块开发之保存微信用户信息...
查看>>
Linux下Nginx安装
查看>>
LVM扩容之xfs文件系统
查看>>
Hbase记录-client访问zookeeper大量断开以及参数调优分析(转载)
查看>>
代码片段收集
查看>>
vue-cli3创建项目时报错
查看>>
输入1-53周,输出1-53周的开始时间和结束时间
查看>>
实验二
查看>>
shell——按指定列排序
查看>>
crash 收集
查看>>
507 LOJ 「LibreOJ NOI Round #1」接竹竿
查看>>
UI基础--烟花动画
查看>>
2018. 2.4 Java中集合嵌套集合的练习
查看>>