当前位置: 首页 > news >正文

【STL map】map用法、插入方法、pair和make_pair的区别

map容器插入数据的4种方法

在构造map容器后,我们就可以往里面插入数据了。

在VC下请加入这条语句,屏蔽4786警告 #pragma warning (disable:4786) )

这里讲四种插入数据的方法:

map<int, string> mapStudent;  
    mapStudent.insert(pair<int, string>(1, "student_one"));             //pair<>()函数
    mapStudent.insert(map<int, string>::value_type (1, "student_one")); //map<>::value_type
    mapStudent.insert(make_pair(1, "student_one"));                     //make_pair()函数
      mapStudent[1] = "student_one";                                      //数组方式


    1、pair<int, string>(1, "student_one")
    2、map<int, string>::value_type (1, "student_one")
    3、make_pair(1, "student_one")
    4、[1] = "student_one"

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

#include <iostream>  
using namespace std;  
  
#include <string>  
#include <map>  
  
map<int,string> mp;  
  
void showMap()  
{  
    cout<<"\n遍历结果:"<<endl;  
    for(map<int,string>::iterator iter = mp.begin();iter != mp.end(); ++iter)  
    {  
        cout<<iter->first<<" - "<<iter->second<<endl;  
    }  
    cout<<endl;  
}  
  
int main()  
{  
    pair<map<int,string>::iterator,bool> myPair;//保存insert()的返回值  
    //方法[1]  
    myPair = mp.insert(pair<int,string> (1,"student01"));  
    if(true == myPair.second)  
    {  
        cout<<"插入("<<myPair.first->first<<","<<myPair.first->second<<")成功."<<endl;  
    }  
    else  
    {  
        cout<<"插入失败! 对应的key值: "<<myPair.first->first<<endl;  
    }  
      
    //方法[2]  
    myPair = mp.insert(make_pair(2,"student02"));  
    myPair = mp.insert(make_pair(2,"student22"));//插入失败,不会产生覆盖  
  
    if(true == myPair.second)  
    {  
        cout<<"插入("<<myPair.first->first<<","<<myPair.first->second<<")成功."<<endl;  
    }  
    else  
    {  
        cout<<"插入失败! 对应的key值: "<<myPair.first->first<<endl;  
    }  
      
    //方法[3]  
    myPair = mp.insert(map<int,string>::value_type(3,"student03"));  
      
    //方法[4]  
    mp[4] = "student04";  
    mp[4] = "student44";//覆盖  
  
    showMap();  
  
    return 0;  
}

前3种方法,采用的是insert()方法,该方法返回的是pair<iterator,bool>,进行重复插入时,插入失败,不会产生覆盖;
第4种方法,插入重复将会覆盖原有的值。

 

make_pair

C++标准程序库中凡是“必须返回两个值”的函数, 也都会利用pair对象 

class pair可以将两个值视为一个单元。容器类别map和multimap就是使用pairs来管理其健值/实值(key/value)的成对元素。 

pair被定义为struct,因此可直接存取pair中的个别值.

两个pairs互相比较时, 第一个元素正具有较高的优先级. 
例: 

namespace std
{
    template <class T1, class T2 >
    bool operator< (const pair<T1, T2> &x, const pair<T1, T2> &y)
    {
        return x.first < y.first || ((y.first < x.first) && x.second < y.second);
    }
}

make_pair():
无需写出型别, 就可以生成一个pair对象 
例: 

std::make_pair(42,  '@');

而不必费力写成:
std::pair<int, char>(42,  '@')

当有必要对一个接受pair参数的函数传递两个值时, make_pair()尤其显得方便, 
void f(std::pair<int, const char *>);

void foo
{ 
    f(std::make_pair(42, '@')); //pass two values as pair
}

pair的应用

pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。

2 make_pair函数

template pair make_pair(T1 a, T2 b) { return pair(a, b); }

很明显,我们可以使用pair的构造函数也可以使用make_pair来生成我们需要的pair。 一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象很方便,代码也很清晰。 另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。灵活度也带来了一些问题如:

std::pair<int, float>(1, 1.1);

std::make_pair(1, 1.1);

是不同的,第一个就是float,而第2个会自己匹配成double。

3.       map的大小

在往map里面插入了数据,我们怎么知道当前已经插入了多少数据呢,可以用size函数,用法如下:

Int nSize = mapStudent.size();

4.       数据的遍历

这里也提供三种方法,对map进行遍历

第一种:应用前向迭代器,上面举例程序中到处都是了,略过不表

第二种:应用反相迭代器,下面举例说明,要体会效果,请自个动手运行程序

#include <map>
#include <string>
#include <iostream>

Using namespace std;

Int main()
{

    Map<int, string> mapStudent;

    mapStudent.insert(pair<int, string>(1, “student_one”));

    mapStudent.insert(pair<int, string>(2, “student_two”));

    mapStudent.insert(pair<int, string>(3, “student_three”));

    map<int, string>::reverse_iterator iter;

    for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)
    {

        Cout << iter->first << ”  ” << iter->second << end;

    }

}

第三种:用数组方式,程序说明如下

#include <map>
#include <string>
#include <iostream>

Using namespace std;

Int main()
{

       Map<int, string> mapStudent;
       
       mapStudent.insert(pair<int, string>(1, “student_one”));
       mapStudent.insert(pair<int, string>(2, “student_two”));
       mapStudent.insert(pair<int, string>(3, “student_three”));

       int nSize = mapStudent.size()

    //此处有误,应该是 for(int nIndex = 1; nIndex <= nSize; nIndex++)
    //by rainfish
     for(int nIndex = 0; nIndex < nSize; nIndex++)

    {

           Cout << mapStudent[nIndex] << end;

    }

}

5.  数据的查找(包括判定这个关键字是否在map中出现)

在这里我们将体会,map在数据插入时保证有序的好处。

要判定一个数据(关键字)是否在map中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的map基本用法。

这里给出三种数据查找方法

第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了

第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器,程序说明

#include <map>
#include <string>
#include <iostream>

Using namespace std;

Int main()
{

    Map<int, string> mapStudent;

    mapStudent.insert(pair<int, string>(1, “student_one”));
    mapStudent.insert(pair<int, string>(2, “student_two”));
    mapStudent.insert(pair<int, string>(3, “student_three”));

    map<int, string>::iterator iter;
    iter = mapStudent.find(1);

    if(iter != mapStudent.end())
    {

        Cout << ”Find, the value is ” << iter->second << endl;

    }
    Else
    {

        Cout << ”Do not Find” << endl;

    }

}

第三种:这个方法用来判定数据是否出现,是显得笨了点,但是,我打算在这里讲解

Lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器)

Upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器)

例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3

Equal_range函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字,程序说明

#include <map>
#include <string>
#include <iostream>
Using namespace std;

Int main()
{

    Map<int, string> mapStudent;

    mapStudent[1] =  “student_one”;
    mapStudent[3] =  “student_three”;
    mapStudent[5] =  “student_five”;
    
    map<int, string>::iterator ? iter;

    iter = mapStudent.lower_bound(2);
    {
        //返回的是下界3的迭代器
        Cout << iter->second << endl;
    }

    iter = mapStudent.lower_bound(3);
    {
        //返回的是下界3的迭代器
        Cout << iter->second << endl;
    }


    iter = mapStudent.upper_bound(2);
    {
        //返回的是上界3的迭代器
        Cout << iter->second << endl;
    }

    iter = mapStudent.upper_bound(3);
    {
        //返回的是上界5的迭代器
        Cout << iter->second << endl;
    }



    Pair<map<int, string>::iterator, map<int, string>::iterator> mapPair;
    mapPair = mapStudent.equal_range(2);

    if(mapPair.first == mapPair.second)
    {
        cout << ”Do not Find” << endl;
    }
    Else
    {

        Cout << ”Find” << endl;
    }

    mapPair = mapStudent.equal_range(3);

    if(mapPair.first == mapPair.second)
    {
        cout << ”Do not Find” << endl;
    }
    Else
    {
        Cout << ”Find” << endl;
    }
}

6.       数据的清空与判空

清空map中的数据可以用clear()函数,判定map中是否有数据可以用empty()函数,它返回true则说明是空map

7.       数据的删除

这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法

#include <map>
#include <string>
#include <iostream>
Using namespace std;

Int main()

{

       Map<int, string> mapStudent;

       mapStudent.insert(pair<int, string>(1, “student_one”));
       mapStudent.insert(pair<int, string>(2, “student_two”));
       mapStudent.insert(pair<int, string>(3, “student_three”));

    //如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好
    //如果要删除1,用迭代器删除
       map<int, string>::iterator iter;
       iter = mapStudent.find(1);
       mapStudent.erase(iter);



       //如果要删除1,用关键字删除
       Int n = mapStudent.erase(1); //如果删除了会返回1,否则返回0

       //用迭代器,成片的删除
       //以下代码把整个map清空
       mapStudent.earse(mapStudent.begin(), mapStudent.end());

       //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合
       //自个加上遍历代码,打印输出吧
}

8.       其他一些函数用法

这里有swap,key_comp,value_comp,get_allocator等函数,感觉到这些函数在编程用的不是很多,略过不表,有兴趣的话可以自个研究

9.       排序

这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过不去,下面给出两个方法解决这个问题

第一种:小于号重载,程序举例

#include <map>
#include <string>
Using namespace std;

Typedef struct tagStudentInfo
{
       Int    nID;
       String  strName;
} StudentInfo, *PStudentInfo;
 //学生信息


Int main()
{
     int nSize;

       //用学生信息映射分数

       map<StudentInfo, int>mapStudent;
       map<StudentInfo, int>::iterator iter;

       StudentInfo studentInfo;
       
       studentInfo.nID     = 1;
       studentInfo.strName = “student_one”;
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));
      
       studentInfo.nID     = 2;
       studentInfo.strName = “student_two”;
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));



    for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
        cout << iter->first.nID << endl << iter->first.strName << endl << iter->second << endl;



}

以上程序是无法编译通过的,只要重载小于号,就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 <map>

#include <string>

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<StudentInfo, int, sort>mapStudent;

       StudentInfo studentInfo;

       studentInfo.nID     = 1;
       studentInfo.strName = “student_one”;
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));

       studentInfo.nID     = 2;
       studentInfo.strName = “student_two”;
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));

}

10.   另外

由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,这里默认用的是小于号,即less<>,如果要从大到小排序呢,这里涉及到的东西很多,在此无法一一加以说明。

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

下面说下,map在空间上的特性,否则,估计你用起来会有时候表现的比较郁闷,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),我想大家应该知道,这些地方很费内存了吧,不说了……

相关文章:

  • 【字符编码】 简洁理解ANSI,UTF8,Unicode,ASCII编码的差别
  • [MYSQL]mysql将两个表结果合并到一起
  • 【MYSQL】mysql把字段进行逗号分隔成多条数据
  • 【python备份】Python备份CSDN完整博客 研读
  • 【python】Python中yield的理解与使用|python生成器
  • 【数据结构】八大数据结构分类 研读笔记
  • 【python】list、tuple、dict、set、dataframe、narray、series之间的区别
  • 【python】python3.7数据分析入门学习笔记 研读
  • 【VS输出UTF8】 C++ 控制台程序中输出UTF8字符乱码问题解决方法
  • 【python】python一些热点问题
  • 【thrift】thrift 研究
  • 【人工智能】我的人工智能之旅——线性回归 研读
  • 【python】Python Web服务器并发性能测试
  • 【protocolbuff】linux下安装google protobuf[实践] --未成功
  • 【ML】之 线性回归(实战) 研读
  • [译]Python中的类属性与实例属性的区别
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • Angular 响应式表单之下拉框
  • download使用浅析
  • extract-text-webpack-plugin用法
  • Fundebug计费标准解释:事件数是如何定义的?
  • Java Agent 学习笔记
  • Java 最常见的 200+ 面试题:面试必备
  • JavaScript对象详解
  • PHP的类修饰符与访问修饰符
  • React组件设计模式(一)
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • Spring Boot快速入门(一):Hello Spring Boot
  • ubuntu 下nginx安装 并支持https协议
  • Vim 折腾记
  • Vue2.0 实现互斥
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 从零开始的无人驾驶 1
  • 当SetTimeout遇到了字符串
  • 聊一聊前端的监控
  • 推荐一个React的管理后台框架
  • 再谈express与koa的对比
  • elasticsearch-head插件安装
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • 积累各种好的链接
  • ​如何在iOS手机上查看应用日志
  • !$boo在php中什么意思,php前戏
  • #Z0458. 树的中心2
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (2)STL算法之元素计数
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (原)本想说脏话,奈何已放下
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .NET Micro Framework初体验(二)
  • .NET 读取 JSON格式的数据
  • .net6 webapi log4net完整配置使用流程
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [2019/05/17]解决springboot测试List接口时JSON传参异常