map、multimaps函数

来源:互联网 发布:淘宝安卓版 编辑:程序博客网 时间:2024/05/22 20:27

1 简介

map  multimap key(键值,或理解为排序关键字)/value(值)作为元素,成对存储,它们可根据key的排序准则自动将元素排序。multimap允许重复键值,maps不允许。

使用mapmultimap需要使用头文件<map>,在使用这个头文件时,要用标准名称空间(using namespace std;)

mapmultimap的元素是 key/value 对,map可以作为映射式数组使用,比如存储学生信息时,学生的学号和姓名就是一一对应的映射关系,或者在存储电话号码时,人名和电话号码也是映射关系,等等。

mapmultimap根据元素的key自动对元素进行排序。根据已知的key搜寻某个元素时,能够有很好的性能,根据已知的value搜寻元素时性能则很糟糕。

“自动排序”这一性质使得mapmultimap 很重要的限制:不能直接改变元素的key,因为这会破坏正确顺序。要修改元素的key,必须先移除拥有该key的元素,然后插入拥有新的key值的元素。

 mapmultimaps的构造函数

   

                   1  mapmultimap的构造函数

 

操作

效果

map c

产生一个空的map/multimap,其中不含任何元素

map c (op)

op为排序准则,产生一个空的map/multimap

map c1(c2)

产生某个map/multimap的副本,所有元素均被复制

map c (beg, end)

以区间[beg ; end]内的元素产生一个map/multimap

map c (beg, end, op)

op为排序准则,利用[beg ; end]内的元素生成一个map/multimap

 

在表1中的map可以是以下形式:

 

 

2  1map的可选形式

 

map

效果

map<key, Elem>

一个map,以less<>(实际上就是小于号“<”)为排序准则

map<key, elem, op>

一个map,以op为排序准则

multimap<keyelem>

一个multimap,以less<>为排序准则

multimap<key, elem, op>

一个multimap,以op为排序准则

 

2中,key为键值类型,elemvalue值的类型。op可以是less<>或者是greater<>,在排序准则中要指定key的类型。

第二个参数指定了value的类型:string

声明一个map容器变量方法如下:

 

第一个参数指定了key的类型:float

 


 

#include <iostream>

#include <map>

#include <cstring>

 

注意:此处两个“>”中间有一个空格

using namespace std;

 

int main()

{

       map<float, string, greater<float> > mymap;

       return 0;

}

 

map<float, string, greater<float> > mymap;语句中,map<float, string, greater<float> >是类型,mymap是该map容器的变量,二者之间的关系就如同int a;语句中,变量类型int和变量a之间的关系。实际上,map是一种容器,这种容器可以存储任何类型的变量,但是具体你需要什么类型的map,还需要告诉编译器,比如在本例的map容器,以float类型为key,以string类型为value。这些是属于泛型编程的内容,具体细节以后接触C++的时候就会明白。

要注意红色高亮部分的两个“>”中间要有一个空格,因为连续的两个“>”会被编译器解释为移位操作符,导致语法错误。

以上程序片段在VC中编译会有一大票C4786警告,不必理会,在其他编译器中无此问题。

 

3 数据的插入和删除

 

3.1 插入数据

 

1、使用pair<>insert语句:

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<float, string, greater<float> > mymap;

       mymap.insert(pair<float, string>(22.3,"hello"));

       mymap.insert(pair<float,const string>(78.5,"ok"));

       return 0;

}

 

其中pair的功能是把pair后面尖括号里指定的两种类型合并为一个pair类型。因为map容器存储数据都是相关联的数据成对存储的,所以要先把两个值合成一个“pair类型”。比如上面例子中,pair<float, string>(22.3, “hello”)实现的功能就是把一个float和一个string合并成一个pair类型变量(实际上这就是面向对象中所谓的“对象”,对象与类的关系类似于c语言中内置类型如int与该类型变量的关系。实际上pair是一个类)。这个pair类型变量的一对值分别是浮点值22.3和字符串”hello”。(注意string也是一个类,和c语言中的c风格字符串不一样,要使用string的话需要包含头文件<cstring>

注意以上代码中黄色高亮部分:”hello””ok”实际上都是const string,而第一条insert语句中的pair指定的是string类型,这时pair会做一个自动的隐式类型转换,把const string类型(也就是”hello”)变成string类型。

 

2、使用make_pair函数和insert,这是最方便的方法。

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<float, string, greater<float> > mymap;

       mymap.insert(pair<float, string>(22.3,"hello"));

       mymap.insert(pair<float,const string>(78.5,"ok"));

       mymap.insert(make_pair(50.0,"happy"));

       return 0;

}

 

make_pair也会执行必要的隐式类型转换。但是在VC6.0中编译时mymap.insert(make_pair(50.0,"happy"));这一句通不过,”happy”被认为是char[6]类型,没有被自动转换成string。在dev c++中编译顺利通过。看来VC6.0这个开发环境存在的主要目的是为基于MFCwin32编程服务的,用到c++核心内容时,会深刻感受到VC6.0的不标准。

 

3、使用value_typeinsert

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<float, string, greater<float> > mymap;

       mymap.insert(pair<float, string>(22.3,"hello"));

       mymap.insert(pair<float,const string>(78.5,"ok"));

       mymap.insert(make_pair(50.0,"happy"));

       mymap.insert(map<float, string>::value_type(53.7,"lucky"));

 

       return 0;

}

 

4 map视为关联式数组,以数组下标方式(即[]

 

非常量(Non-const)map提供下标操作符以支持元素的直接存取。如表3

 

操作

效果

m[key]

返回一个引用(C++中的一种新类型,是一个变量的别名,可以按照指针来理解,但是使用起来比指针简单,而且效率比指针高),该引用指向键值为key的元素。如果该元素尚未存在,则插入该元素。

 

注意m[key]中的索引值”key”不一定是整型,因为他是map容器每个元素的key,可以是任意类型,这样可以方便地实现关联式数组。

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       return 0;

}

 

以上代码黄色高亮部分中,第一句

anothermap["lisi"]=1.77;

anothermap中已经存在了key值为”lisi”的元素,那么这一句将会用1.77覆盖掉原来lisi所对应的值(1.72)

黄色高亮部分中,第二句

       anothermap["wangwu"]=1.66;

another中不存在key值为”wangwu”的元素,那么会向anothermap中自动插入一个新元素,key值为”wangwu”,并把这个新插入的元素的value设置为某个默认值(比如当value的类型为int时,会默认设置为0),然后再把1.66赋给这个新插入元素的value

 

前面说过,map中不能存在key值相同的元素。因此,在使用insert添加map新元素时,如果新插入元素的key值与容器中原有元素的key值有重复,则insert会失败。但使用下标方式插入则不会出现插入失败,因为如果在原有元素中存在与新插入元素key值相同的元素,则会用新插入元素的value去覆盖掉原有元素的value。这是下标方式的优点,也是其缺点,比如这会导致我们错误地覆盖掉容器中的原有元素,或是不小心错误插入新元素。比如:

cout<<anothermap[“zhangsa”];

在输入”zhangsan”时,少敲了一个”n”,这将会导致向anothermap中插入一个新值,其key”zhangsa”value0

另外,以下标方式向map中添加元素的速度要比insert方式许多。

 

4 迭代器(iterator)

 

STL中,迭代器的角色类似于C/C++中的指针。其声明方式如下:

map<string,float>::iterator pos;

其中map<string,float>表明这个迭代器的类型,map<string,float>::iterator pos表示声明一个迭代器pos,其类型为map<string,float>

迭代器用法:

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

      

       map<string,float>::iterator pos;

      

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

          

       }

 

       return 0;

}

 

黄色高亮部分第一句声明了迭代器pos。第二句在for循环中,首先用到了map容器的begin()成员函数,该函数返回调用该函数的对象(也就是anothermap)的第一个元素的迭代器(可以理解为指针);end()函数返回的迭代器指向了调用该函数的对象的最后一个元素后面的位置。如下图:

 

图中容器中共有7个元素a, b, c, d, e, f begin()所返回的迭代器指向aend()所返回的迭代器指向最后元素g的后面的位置。

另外需要注意的是for循环中的循环终止条件不是pos<anothermap.end()而是pos!=anothermap.end()具体原因大家以后学到操作符重载的时候就会明白。

 

5 keyvalue值的访问

 

当迭代器pos指向map容器中某个元素,以下表达式

pos->first

获得该元素的key

以下表达式

pos->second

获得该元素的value

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

       return 0;

}

其输出结果如下:

 

不能通过pos->first这种方式改变元素的key

pos->first=”xuqi”;    //编译时出错

记住最前面提到的:不能直接改变元素的key,因为这会破坏正确顺序。要修改元素的key,必须先移除拥有该key的元素,然后插入拥有新的key值的元素。

如果元素本身value的值非const,可以通过pos->second来改变value的值,如下面的语句:

pos->second=1.73;        //OK

 

6 容器元素的删除

 

6.1 通过erase()函数删除具有某个key值的单个元素

 

如:要删除容器中拥有某个key值的元素,可以通过以下方式:

anothermap.erase("lisi");

完整代码如下:

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

cout<<endl;

 

       anothermap.erase("lisi");

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

 

       return 0;

}

 

运行结果如下:

 

可以看到,key”lisi”的元素已经从anothermap对象中被删除。

 

6.2 通过erase()函数删除某个位置上的元素

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

    cout<<endl;

 

       pos = anothermap.find("lisi");

       if(pos!=anothermap.end())

        anothermap.erase(pos);

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

       return 0;

}

运行结果:

 

高亮部分的find()函数用来搜寻拥有某个key的第一个元素,并返回一个指向该元素的迭代器。如果没有找到这样的元素,就返回该容器的end()。注意,不能用find()搜索拥有某特定value的元素。

如果我们需要删除map中具有某个value值的元素,可以通过如下方式:

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

    cout<<endl;

 

    map<string,float>::iterator temp;

 

       for(pos = anothermap.begin(); pos!= anothermap.end();pos++)

        if(pos->second==1.75)

        {

            temp=pos;

               anothermap.erase(temp);

            break;

        }

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

 

       return 0;

}

 

运行结果:

 

 

首先要知道的是,不能通过以下方式删除具有某value值的元素:

for(pos=anothermap.begin();pos!=anothermap.end();pos++)

       if(pos->second==1.75)

              anothermap.erase(pos);

因为对pos所指元素实施erase()之后,会使pos不再成为一个有效的anothermap迭代器,从而pos成为一种未定义的状态。因此,要删除具有某个value的元素,需要按照上面高亮部分代码所做的:声明另一个该类型的迭代器temp,用pos去遍历整个容器的所有元素,当发现第一个具有value值的元素时,把pos值赋给temp,然后用temp去删除。

 

6.3 使用erase()移除某个区间内所有元素

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

    cout<<endl;

 

    map<string,float>::iterator ibeg;

    map<string,float>::iterator iend;

 

    for(ibeg=anothermap.begin();ibeg!=anothermap.find("lisi");ibeg++);

    for(iend=anothermap.begin();iend!=anothermap.find("zhangsan");iend++);

    anothermap.erase(ibeg,iend);

 

 

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

 

       return 0;

}

运行结果如下:

 

可以看到,在容器中key”lisi”key”zhangsan”之间的元素被删除。注意,删除区间包括”lisi”但不包括”zhangsan”

 

6.4 使用clear移除全部元素,将整个容器清空

 

#include <iostream>

#include <map>

#include <cstring>

using namespace std;

 

int main()

{

       map<string,float,less<string> > anothermap;

 

       anothermap.insert(make_pair("zhangsan",1.75));

       anothermap.insert(make_pair("lisi",1.72));

       anothermap.insert(make_pair("zhaoliu",1.69));

 

       anothermap["lisi"]=1.77;

       anothermap["wangwu"]=1.66;

 

       map<string,float>::iterator pos;

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

    cout<<endl;

 

    anothermap.clear();

 

       for(pos = anothermap.begin();pos!=anothermap.end();pos++)

       {

        cout<<pos->first<<" "<<pos->second<<endl;

       }

 

 

       return 0;

}

运行结果如下:

 

 

7 key值为用户自定义类型时,map的用法

 

因为map容器会自动对存入容器的元素进行排序,排序规则由

multimap<key, elem, op>中的op指定,op可以是less也可以是greater,其实就是小于号“<”和大于号“>”,如果不指定op,则op取默认值less。当op指定为less时,容器中所有元素按升序排列,opgreater时,容器中元素按降序排列。

问题在于,当key值为内置类型,如intfloat等类型时,编译器知道如何比较这些类型的值的大小,比如声明两个整型变量

int a,b;

当有表达式a<b时,编译器知道如何比较ab,从而正确地给出该表达式的值,为真或为假。

但是当key值为用户自定义类型(结构体,类),比如下面的结构体,用来存储学生的学号和身高:

struct stu

{

       int num;

       float height;

};

声明两个stu结构体变量:

stu stu1,stu2;

如果给出如下表达式stu1>stu2,编译器是否知道如何比较这两个结构体变量?

因为编译器不知道如何比较用户自定义类型,因此当key值为用户自定义类型时,map将无法实现在存储时自动排序。比如有如下代码:

#include <iostream>

#include <map>

using namespace std;

 

struct stu

{

    int num;

    float height;

};

 

int main()

{

    stu stu1,stu2;

    stu1.num=1;

    stu1.height=1.75;

    stu2.num=2;

    stu2.height=1.65;

 

    map<stu,int> stumap;

 

    stumap.insert(make_pair(stu1,22));

    stumap.insert(make_pair(stu2,23));

 

    return 0;

}

 

编译时,会提示如下错误:

 

意思是,没有适合用来比较比较xy的“小于号操作符(operator<)”这里的xy实际就是上面代码中的stu1stu2。实际上,编译器不知道怎样比较stu这种类型。

解决这个问题要用到一种叫做“操作符重载”的技术。

实际上,小于号(<),大于号(>)和等于号(= =)都是函数,它们的返回值类型都是bool类型。它们的函数原型分别是:

operator<(参数)operator>(参数)operator==(参数)

举例来说:

int a,b;

a=4;

b=5;

当我们使用a>b这个表达式时,实际上是调用了operator>()这个函数,a>b可以写成这样:

a.operator>(b);

变量a加一个点符号是什么意思?想想结构体变量如何引用它们的成员?

struct stu

{

    int num;

    float height;

};

stu stu1;

stu1.num=5;

结构体变量stu1用“.”符号引用它的num成员,但是以前我们在c语言里学过的结构体的成员都只能是某种类型的变量,在C++中,结构体的成员也可以是函数。我们可以把内置类型看做是一些特殊的结构体,比如int,可以假设它是这样定义的:

struct int

{

       bool operator>(int x);

       bool operator<(int x);

       bool operator==(int x);  //这些就是int类型的成员函数

       .

       .//还有其他一些操作符

};

operator>(int x)函数可以如下定义:

bool operator(int x)

{

       return a>x;

}

如果a大于形参x,那么返回值为true,也就是表达式a>x的结果为真,否则的话,函数operator>()的返回值为false,也就是表达式a>x的结果为假

现在,我们可以弄清a.operator>(b)是什么意思了。这句话的意思是,用变量a调用函数operator>这个函数,函数的实参为b,对于int类型,编译器知道如何比较大小,因此这次调用的返回值是false

现在我们返回操作符重载的问题。操作符重载的目的,就是告诉编译器如何比较用户自定义的类型。我们也可以把操作符重载叫做“操作符重定义”。

对于

struct stu

{

    int num;

    float height;

};

stu stu1,stu2;

如果我们需要当stu1的学号小于stu2的学号时,编译器就判定stu1小于stu2,那么我们可以重新定义”<”操作符的行为:

#include <iostream>

#include <map>

using namespace std;

 

struct stu

{

    int num;

    float height;

    bool operator<(const stu & x) const             //注意两个const

    {

        return num<x.num;       //num是调用该成员函数的结构体变量自身的num成员

    }

};

 

 

 

int main()

{

    stu stu1,stu2;

    stu1.num=1;

    stu1.height=1.75;

    stu2.num=2;

    stu2.height=1.65;

 

    map<stu,int> stumap;

 

    stumap.insert(make_pair(stu1,22));

    stumap.insert(make_pair(stu2,23));

 

    map<stu,int>::iterator it;

    for(it=stumap.begin();it!=stumap.end();it++)

    {

        cout<<it->second<<endl;

    }

 

    return 0;

}

 

    以上代码中,stumap对于key的排序是默认的less,如果我们指定排序规则为greater,就需要重载”>”操作符,大家可以自己练习一下试试。 

0 0