STL中map的用法剖析
- 格式:doc
- 大小:28.50 KB
- 文档页数:6
STL中map的用法剖析摘要本文深入剖析了C++标准模板库(STL)中的map,对其概念和用法进行了深入探讨,并结合实例,详细阐述了map的相关用法。
关键词STL;map;插入;删除;排序1 map 概述STL(Standard Template Library 标准模版库)是C++标准程序库的核心,它深刻影响了标准程序库的整体结构。
STL是一个范型(generic)程序库,提供一系列软件方案,利用先进、高效的算法来管理数据。
STL 的好处在于封装了许多数据结构和算法(algorithm),map就是其典型代表。
map是STL的一个关联容器,它提供一对一(key/value 其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可以称为该关键字的值)的数据处理能力,由于这个特性,在处理一对一数据的时候,可以提供编程的快速通道。
2 map 的用法假设一个班级中,每个学生的学号和他的姓名存在一一映射的关系,这个模型用map 可以轻易描述,学号用int 描述,姓名用字符串描述,给出map 的描述代码:map<int,string> mapStudent 。
2.1 插入数据map元素的插入功能可以通过以下操作实现:第一种通过主键获取map中的元素,如果获取到,则返回对应结点对应的实值(存储在map 结点中的对象)。
但这个方法会产生副作用,如果以主键“key”获取结点的实值,在map 中并不存在这个结点,则会直接向map 中插入以key 为主键的结点,并返回这个结点,这时可以对其进行赋值操作。
但如果在map 中存在了以key 为主键的结点,则会返回这个结点的实值,如果此时进行复制操作,则会出现原来结点被新结点覆盖的危险,如果是指针类型则会出现内存泄漏等问题。
由于存在这样的副作用,不建议使用这种方法进行元素的插入。
第二种插入value_type数据。
insert 方法接口原型:pair<ierator,bool> insert(constvalue_type& X)该方法需要构建一个键值对,即value_type,然后调用insert方法,在该方法中实现根据键值对中的key值查找对应的结点,如果查找到,则不插入当前结点,并返回找到的那个结点,并将pair 中的第二个量置为false;否则插入当前结点,并返回插入的当前结点,且第二个值置为true。
在插入结点的时候,在map 内部会重新构造一个新的value_type 结点并将传入的X 进行copy 构造,内部使用了placement new方式,通过内存分配器分配一个map结点,再在获取的结点空间中调用value_type 构造函数。
所以调用者构造的键值对value_type 是一个临时变量,不会加入到map 中(不要被引用操作符迷惑,这里仅仅是传参效率上的考虑)。
这种结点插入的方式是安全的,建议使用这种方式向map中插入元素,并判断返回的插入结果,根据插入结果进行后续处理。
#pragma warning(disable:4786)#include <map>#include <string>#include <iostream>using namespace std;int main(){map<int,string> mapStudent;mapStudent.insert(map<int,string>::value_type(1,"one"));mapStudent.insert(map<int,string>::value_type(2,"two"));mapStudent.insert(map<int,string>::value_type(3,"three"));map<int,string>::iterator iter;for(iter=mapStudent.begin();iter!=mapStudent.end();iter++) {cout<<iter->first<<" "<<iter->second<<endl;}}2.2 map的大小往map中插入了数据,可以用size()函数得到当前已经插入了多少数据:int nSize=mapStudent.size()2.3 排序STL 中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面例子中的关键字是int型,它本身支持小于号运算。
在一些特殊情况下,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号运算,insert等函数在编译的时候过不去。
给出一种方法解决排序问题——小于号重载。
#pragma warning(disable:4786)#include <map>#include <string>#include <iostream>using namespace std;typedef struct tagStudentInfo{int nID;string strName;} StudentInfo,*PStudentInfo;// 学生信息int main(){//用学生信息映射分数map<StudentInfo,int> mapStudent;StudentInfo studentInfo;studentInfo.nID=2;studentInfo.strName="one";mapStudent.insert(map<StudentInfo ,int> ::value_type (studentInfo,90));studentInfo.nID=1;studentInfo.strName="two";mapStudent.insert(map<StudentInfo ,int> ::value_type (studentInfo,80));}以上程序无法编译通过,需要重载小于号。
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 pare(_A.strName)<0;return false;}} StudentInfo,*PStudentInfo;2.4 map中结点的删除操作两种应用场景:第一种:一次只从map中查找一个结点并删除。
这种删除较为简单,只需要根据键值在map中查找,并将找到的结点删除就可以了。
#pragma warning(disable:4786)#include <map>#include <string>#include <iostream>using namespace std;void main(){map<int,string> mapStudent;mapStudent.insert(map<int,string>::value_type(1,"one"));mapStudent.insert(map<int,string>::value_type(2,"two"));mapStudent.insert(map<int,string>::value_type(3,"three"));map<int,string>::iterator iter;mapStudent.erase(iter);for(iter=mapStudent.begin();iter!=mapStudent.end();iter++){cout<<iter->first<<" "<<iter->second<<endl;}}第二种:从map中遍历检查所有结点,将符合条件的结点删除。
应用场景描述:系统定期检查垃圾会话(会话放在map表中),根据当前系统时间减去会话最近活动时间,得到会话最近未活动时间间隔,如果这个间隔超过预定的值(认为会话是垃圾会话,可以被强制删除),则删除该会话并从map中删除这个结点。
做法有两种:(1)先遍历一遍map,找出所有满足条件的结点,将每一个对应结点的key 放入一个vector 中,后面再从vector 中依次取出key值,做单结点删除操作。
这种方法是很原始且效率低下的做法,之所以会这样实现,是由于开发人员对map使用不甚了解的基础上做出来的。
这种方法不但增加了中间处理过程的系统开销(多构建了一个缓存空间),而且多了N(待删除结点的结点数)次的查询操作,对于经常出现的操作,这种低效是不可容忍的。
(2)在map 遍历的过程中,完成对符合条件结点的删除操作(这个是由map 本身数据结构特性保证的)。
在遍历的过程中最主要的就是怎么保证删除的结点在删除前将指针指向下一个结点(这一点正是我们要做的),在删除了当前结点后,map中的数据结构能够保证后续的迭代器指针是有效的,而且后续的结点都没有遍历过(这个特性是由map 底层的红经验与交流黑树的相关操作保证的)。
所以需要将迭代器指向下一个结点后再删除当前符合条件的结点。
#pragma warning(disable:4786)#include <map>#include <string>#include <iostream>using namespace std;void main(){map<int,string> mapStudent;mapStudent.insert(map<int,string>::value_type(1,"one"));mapStudent.insert(map<int,string>::value_type(2,"two"));mapStudent.insert(map<int,string>::value_type(3,"three"));map<int,string>::iterator iter;string aa="three";for(;iter!=mapStudent.end();){if((iter->second)>=aa){//满足删除条件,删除当前结点,并指向下面一个结点mapStudent.erase(iter++);}else{//条件不满足,指向下面一个结点iter++;}}for(iter=mapStudent.begin();iter!=mapStudent.end();iter++) {cout<<iter->first<<" "<<iter->second<<endl;}}这种删除方式也是STL源码一书中推荐的方式。