【C++ STL】Map和Multimap
1、结构
Map和multimap将key/value pair(键值/实值 队组)当作元素,进行管理。他们根据key的排序准则将元素排序。multimap允许重复元素,map不允许。
元素要求:
- key/value必须具有assigned(可赋值)和copyable(可复制的)性质。
- 对于排序而言,key必须具是comparable(可比较的)。
2、能力
典型情况下,set,multisets,map,multimap使用相同的内部结构,因此可以将set和multiset当成特殊的map和multimap,只不过set的value和key指向的是同一元素。
map和multimap会根据key对元素进行自动排序,所以根据key值搜寻某个元素具有良好的性能,但是如果根据value来搜寻效率就很低了。
同样,自动排序使得你不能直接改变元素的key,因为这样会破坏正确次序,要修改元素的key,必须先移除拥有key的元素,然后插入拥有新的key/value的元素,从迭代器的观点来看,元素的key是常数,元素的value是可以直接修改的。
3、操作函数
3.1 构造、析构、拷贝
操作 |
效果 |
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 |
c.~map() |
销毁所有元素,释放内存 |
map<Key,Elem> |
一个map/multimap,以less<> (operator <)为排序准则 |
map<Key,Elem,Op> |
一个map,以op为排序准则 |
3.2 非变动性操作
操作 |
效果 |
c.size() |
返回当前的元素数量 |
c.empty() |
判断大小是否为零,等同于0 == size(),效率更高 |
c.max_size() |
返回能容纳的元素最大数量 |
c1 == c2 |
判断c1是否等于c2 |
c1 != c2 |
判断c1是否不等于c2(等同于!(c1==c2)) |
c1 < c2 |
判断c1是否小于c2 |
c1 > c2 |
判断c1是否大于c2 |
c1 <= c2 |
判断c1是否小于等于c2(等同于!(c2<c1)) |
c1 >= c2 |
判断c1是否大于等于c2 (等同于!(c1<c2)) |
3.3 特殊搜寻函数
操作 |
效果 |
count(key) |
返回键值等于key的元素个数 |
find(key) |
返回键值等于key的第一个元素,没找到返回end() |
lower_bound(key) |
返回键值为key之元素的第一个可安插位置,也就是键值>=key的第一个元素位置 |
upper_bound(key) |
返回键值为key之元素的最后一个可安插位置,也就是键值>key的第一个元素位置 |
equal_range(key) |
返回键值为key之元素的第一个可安插位置和最后一个可安插位置,也就是键值==key的元素区间 |
3.4 赋值
操作 |
效果 |
c1 = c2 |
将c2的元素全部给c1 |
c1.swap(c2) |
将c1和c2 的元素互换 |
swap(c1,c2) |
同上,全局函数 |
3.5 迭代器函数
map和multimap不支持元素直接存取,因此元素的存取通常是经过迭代器进行。不过例外的是,map提供下标操作,可直接存取元素。同样,他们的迭代器是双向迭代器,所有的元素的key都被视为常数,不能调用任何变动性操作,如remove(),要移除元素,只能用塔门提供的函数。
操作 |
效果 |
c.begin() |
返回一个随机存取迭代器,指向第一个元素 |
c.end() |
返回一个随机存取迭代器,指向最后一个元素的下一个位置 |
c.rbegin() |
返回一个逆向迭代器,指向逆向迭代的第一个元素 |
c.rend() |
返回一个逆向迭代器,指向逆向迭代的最后一个元素的下一个位置 |
3.6 元素的安插和移除
操作 |
效果 |
c.insert(elem) |
插入一个elem副本,返回新元素位置,无论插入成功与否。 |
c.insert(pos, elem) |
安插一个elem元素副本,返回新元素位置,pos为收索起点,提升插入速度。 |
c.insert(beg,end) |
将区间[beg,end)所有的元素安插到c,无返回值。 |
c.erase(elem) |
删除与elem相等的所有元素,返回被移除的元素个数。 |
c.erase(pos) |
移除迭代器pos所指位置元素,无返回值。 |
c.erase(beg,end) |
移除区间[beg,end)所有元素,无返回值。 |
c.clear() |
移除所有元素,将容器清空 |
有三个不同的方法将value插入map:
- 运用value_type,为了避免隐式转换,可以使用value_type明白传递正确型别。value_type是容器本身提供的型别定义
- std::map<std::string,float> coll;
- ...
- coll.insert(std::map<std::string,float>::value_type("otto",22.3));
- 运用pair,另一个作法是直接运用pair<>。
- std::map<std::string,float> coll;
- ...
- //use implicit conversion:
- coll.insert(std::pair<std::string,float>("otto",22.3));
- //use no implicit conversion:
- coll.insert(std::pair<const std::string,float>("otto",22.3));
- 上述第一个insert()语句内的型别并不正确,所以会被转换成真正的型别。
- 运用make_pair()
- 最方便的方法是直接运用make_pair()函数,这个函数根据传入的两个参数构造一个pair对象。
- std::map<std::string,float> coll;
- ...
- coll.insert(std::make_pair("otto",22.3));
- 下面是个简单例子,检查安插元素是否成功:
- std::map<std::string,float> coll;
- ...
- if (coll.insert(std::make_pair("otto",22.3)).second) {
- std::cout << "OK, could insert otto/22.3" << std::endl;
- }
- else {
- std::cout << "Oops, could not insert otto/22.3 "
- << "(key otto already exists)" << std::endl;
- }
如果要移除某个值为value的元素,使用erase()即可。
- std::map<std::string,float> coll;
- ...
- //remove all elements with the passed key
- coll.erase(key);
如果multimap内喊重复元素,不能使用erase()来删除这些元素的第一个,但是可以这么做:
- typedef multimap<string.float> StringFloatMMap;
- StringFloatMMap coll;
- ...
- //remove first element with passed key
- StringFloatMMap::iterator pos;
- pos = coll.find(key);
- if (pos != coll.end()) {
- coll.erase(pos);
- }
这里使用成员函数的find()而非STL里面的find(),因为成员函数更快,然而不能使用成员函数find()来移除拥有某个value(而非某个key)的元素。
移除元素时,要当心心意外发生,当移除迭代器所指对象时,可能使迭代器失效。
- typedef std::map<std::string,float> StringFloatMap;
- StringFloatMap coll;
- StringFloatMap::iterator pos;
- ...
- for (pos = coll.begin(); pos != coll.end(); ++pos) {
- if (pos->second == value) {
- coll. erase (pos); // RUNTIME ERROR !!!
- }
- }
对pos所指元素实施erase(),会使pos不再成为一个有效的迭代器,如果此后未对pos重新设值就使用pos,会出现异常。++pos就能导致一个未定义行为。下面是正确的删除方法。
- typedef std::map<std::string,float> StringFloatMap;
- StringFloatMap coll;
- StringFloatMap::iterator pos;
- ...
- //remove all elements having a certain value
- for (pos = c.begin(); pos != c.end(); ) {
- if (pos->second == value) {
- c.erase(pos++);
- }
- else {
- ++pos;
- }
- }
注意,pos++会指向下一个元素,但返回其原始值(指向原位置)的一个副本,因此,当erase()被调用,pos已经不指向那个即将被删除的元素了。
4、map视为关联数组
通常,关联数组不提供直接存取,必须依靠迭代器,不过map是个例外,map提供下标操作符,可以直接存取元素。不过下标操作符的索引不是元素整数位置,而是元素的key。也就是说,索引可以是任何型别,而非局限的整数型别。
操作 |
效果 |
m[key] |
返回一个reference,指向键值为key的元素,如果该元素尚未存在,插入该元素。 |
和一般数组的区别不仅仅是索引型别,你不能使用一个错误的索引,如果你是用某个key为索引,容器尚未存在该元素,会自动安插该元素,新元素由default构造,所有基本型别都提供default构造函数,以零为初始值。
关联数组的行为方式有优点,也有缺点:
优点是可以透过方便的接口向map安插新元素。
- std::map<std::string,float> coll; // empty collection
- /*insert "otto"/7.7 as key/value pair
- *-first it inserts "otto"/float()
- *-then it assigns 7.7
- */
- coll["otto"] = 7.7;
其中的语句:coll["otto"] = 7.7;处理如下:
1.处理coll["otto"]:
--如果存在键值为“otto”的元素,以上式子返回该元素的reference。
--如果没有任何元素的键值为“otto”,以上式子便为map自动安插一个新元素,键值key为“otto”,value通过default构造函数完成,并返回一个reference指向新元素。
2.将7.7赋值给value:
--紧接着,将7.7赋值给刚才返回的元素。
这样,map之内就包含了一个键值(key)为“otto”的元素,其值(value)为7.7。
缺点就是你可能不小心误置新元素。例如你想打印key为“otto”的元素值:
std::cout << coll[“ottto”] << endl;
它会安插一个键值为“ottto”的元素,然后打印其值,缺省情况下是0。它并不会告诉你拼写错误,并且插入一个你可能不需要的元素。
这种插入方式比一般的map安插方式来得慢,因为在生成新元素的时候,需要使用default构造函数将新元素初始化,而这个值马上又会被真正的value覆盖。
5、示例代码
5.1 map
- // cont/mapl.cpp
- #include <iostream>
- #include <map>
- #include <string>
- using namespace std;
- int main()
- {
- /*create map/associative array
- *-keys are strings
- *-values are floats
- */
- typedef map<string,float> StringFloatMap;
- StringFloatMap stocks; // create empty container
- //insert some elements
- stocks["BASF"] = 369.50;
- stocks["VW"] = 413.50;
- stocks["Daimler"] = 819.00;
- stocks["BMW"] = 834.00;
- stocks["Siemens"] = 842.20;
- //print all elements
- StringFloatMap::iterator pos;
- for (pos = stocks.begin(); pos != stocks.end(); ++pos) {
- cout << "stock: " << pos->first << "\t"
- << "price: " << pos->second << endl;
- }
- cout << endl;
- //boom (all prices doubled)
- for (pos = stocks.begin(); pos != stocks.end(); ++pos) {
- pos->second *= ;
- }
- //print all elements
- for (pos = stocks.begin(); pos != stocks.end(); ++pos) {
- cout << "stock: " << pos->first << "\t"
- << "price: " << pos->second << endl;
- }
- cout << endl;
- /*rename key from "VW" to "Volkswagen"
- *-only provided by exchanging element
- */
- stocks["Volkswagen"] = stocks["VW"];
- stocks.erase("VW");
- //print all elements
- for (pos = stocks.begin(); pos != stocks.end(); ++pos) {
- cout << "stock: " << pos->first << "\t"
- << "price: " << pos->second << endl;
- }
- }
输出:
- stock: BASF price: 369.5
- stock: BMW price:
- stock: Daimler price:
- stock: Siemens price: 842.2
- stock: VW price: 413.5
- stock: BASF price:
- stock: BMW price:
- stock: Daimler price:
- stock: Siemens price: 1684.4
- stock: VW price:
- stock: BASF price:
- stock: BMW price:
- stock: Daimler price:
- stock: Siemens price: 1684.4
- stock: Volkswagen price:
5.2 multimap
- // cont/mmap1.cpp
- #include <iostream>
- #include <map>
- #include <string>
- #include <iomanip>
- using namespace std;
- int main()
- {
- //define multimap type as string/string dictionary
- typedef multimap<string,string> StrStrMMap;
- //create empty dictionary
- StrStrMMap dict;
- //insert some elements in random order
- dict.insert(make_pair("day","Tag"));
- dict.insert(make_pair("strange","fremd"));
- dict.insert(make_pair("car","Auto"));
- dict.insert(make_pair("smart","elegant"));
- dict.insert(make_pair("trait","Merkmal"));
- dict.insert(make_pair("strange","seltsam"));
- dict.insert(make_pair("smart","raffiniert"));
- dict.insert(make_pair("smart","klug"));
- dict.insert(make_pair("clever","raffiniert"));
- //print all elements
- StrStrMMap::iterator pos;
- cout.setf (ios::left, ios::adjustfield);
- cout << ' ' << setw() << "english "
- << "german " << endl;
- cout << setfil('-') << setw() << ""
- << setfil(' ') << endl;
- for (pos = dict.begin(); pos != dict.end(); ++pos) {
- cout << ' ' << setw() << pos>first.c_str()
- << pos->second << endl;
- }
- cout << endl;
- //print all values for key "smart"
- string word("smart");
- cout << word << ": " << endl;
- for (pos = dict.lower_bound(word);
- pos != dict.upper_bound(word); ++pos) {
- cout << " " << pos->second << endl;
- }
- //print all keys for value "raffiniert"
- word = ("raffiniert");
- cout << word << ": " << endl;
- for (pos = dict.begin(); pos != dict.end(); ++pos) {
- if (pos->second == word) {
- cout << " " << pos->first << endl;
- }
- }
- }
输出:
- english german
- --------------------
- car Auto
- clever raffiniert
- day Tag
- smart elegant
- smart raffiniert
- smart klug
- strange fremd
- strange seltsam
- trait Merkmal
- smart:
- elegant
- raffiniert
- klug
- raffiniert:
- clever
- smart
【C++ STL】Map和Multimap的更多相关文章
- STL Map和multimap 容器
STL Map和multimap 容器 map/multimap的简介 map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供 基于key的快速检索能力. ...
- C++ STL——map和multimap
目录 一 map和multimap 注:原创不易,转载请务必注明原作者和出处,感谢支持! 一 map和multimap map相对于set的区别:map具有键值和实值,所有元素根据键值自动排序.pai ...
- [STL] map,multimap,unordered_map基本用法
map的特性是,所有元素都会根据元素的键值自动被排序.map的所有元素都是pair,同时拥有键值(key)和实值(value).pair的第一元素被视为键值,第二元素被视为实值.map不允许两个元素拥 ...
- STL(六)——map、multimap
STL--map.multimap 文章目录 STL--map.multimap 关联容器与map的介绍 map与set的异同 map与multimap的异同 map类对象的构造 map添加元素 ma ...
- STL的基本使用之关联容器:map和multiMap的基本使用
STL的基本使用之关联容器:map和multiMap的基本使用 简介 map 和 multimap 内部也都是使用红黑树来实现,他们存储的是键值对,并且会自动将元素的key进行排序.两者不同在于map ...
- STL之Map和multimap容器
1.Map和multimap容器 1)map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供基于key的快速检索能力. 2)map中key值是唯一的.集合中的元素按一 ...
- C++ STL 学习笔记__(8)map和multimap容器
10.2.9 Map和multimap容器 map/multimap的简介 ² map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供基于key的快速检索能力. ² ...
- STL标准库-容器-map和multimap
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 map与multimap为关联容器,结构如下 map底层实现依然是rb_tree 他的data可以改,但是key不能改,因此ma ...
- STL中 map 和 multimap
1. 所在头文件<map>. 命名空间std, 声明如下: namespace std{ template <class Key,class T, class Compare = l ...
随机推荐
- POJ 3714 Raid(计算几何の最近点对)
Description After successive failures in the battles against the Union, the Empire retreated to its ...
- NProgress.js加载进度插件的简单实用方法
NProgress.js 说明: NProgress是基于jquery的,且版本要 >1.8 下载地址: https://github.com/rstacruz/nprogress API: N ...
- OJ错误命令解释
①Presentation Error (PE) : 虽然您的程序貌似输出了正确的结果,但是这个结果的格式有点问题. 请检查程序的输出是否多了或者少了空格(' ').制表符('\t')或者换行符('\ ...
- maven把项目打包成jar包后找不到velocity模板的bug
使用springmvc 开发时候要实现发送velcotiy模板邮件,在配置正常后,在本地测试正常后,使用maven打包成jar包后,报以下错误, Caused by: org.apache.veloc ...
- C# 如何在winform中嵌入Excel,内嵌Excel,word
近使用.net做一个小软件遇到一个问题,就是想实现把excel表格在winform中打开,同时可以操作,不单单是打开.或者就提取数据.在网上找了好多资料,发现这方面的资料比较少,即使有,都是旧版本的使 ...
- 【Docker】- 基本命令
1.docker ps -a 显示所有容器 2.doker ps -l 显示最近一次启动的容器 3.docker ps 显示正在运行的容器 4.docker start [容器ID] 启动 ...
- React Components Template
React Components Template "use strict"; /** * * @author xgqfrms * @license MIT * @copyrigh ...
- 【bzoj4326】[NOIP2015]运输计划 二分答案+LCA
题目描述 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该 ...
- springboot 在tomcat中启动两次
我开始以为眼花了,tomcat启动的时候, . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ...
- [NOI2006]网络收费
题面在这里 description 一棵\(2^n\)个叶节点的满二叉树,每个节点代表一个用户,有一个预先的收费方案\(A\)或\(B\); 对于任两个用户 \(i,j(1≤i<j≤2^n)i, ...