首页 > 其他分享 >map容器

map容器

时间:2024-04-23 10:22:05浏览次数:23  
标签:map 迭代 insert 容器 键值 pair

作为关联式容器的一种,map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括C++基本数据类型(int、double 等)、使用结构体或类自定义的类型。

与此同时,在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less<T>排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用STL标准库中提供的其它排序规则(比如std::greater<T>),也可以自定义排序规则。

另外需要注意的是,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。换句话说,map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改。

1. 模板定义

map 容器的模板定义如下:

template < class Key,                                     // 指定键(key)的类型
           class T,                                       // 指定值(value)的类型
           class Compare = less<Key>,                     // 指定排序规则
           class Alloc = allocator<pair<const Key,T> >    // 指定分配器对象的类型
           > class map;

2. 创建方法

通过调用 map 容器类的默认构造函数,可以创建出一个空的 map 容器,比如:

std::map<std::string, int> myMap;

当然在创建 map 容器的同时,也可以进行初始化,比如:

std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} };

除此之外,在某些场景中,可以利用先前已创建好的 map 容器,再创建一个新的 map 容器。例如:

std::map<std::string, int>newMap(myMap);

C++ 11 标准中,还为 map 容器增添了移动构造函数。当有临时的 map 对象作为参数,传递给要初始化的 map 容器时,此时就会调用移动构造函数。举个例子:

// 创建一个会返回临时 map 对象的函数
std::map<std::string,int> disMap() {
    std::map<std::string, int>tempMap{ {"C语言教程",10},{"STL教程",20} };
    return tempMap;
}
// 调用 map 类模板的移动构造函数创建 newMap 容器
std::map<std::string, int>newMap(disMap());

map 类模板还支持取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器。例如:

std::map<std::string, int>myMap{ {"C语言教程",10},{"STL教程",20} };
std::map<std::string, int>newMap(++myMap.begin(), myMap.end());

3. 成员函数

成员方法 功能
begin() 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
find(key) 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(key) 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(key) 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前 map 容器中存有键值对的个数。
max_size() 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
operator[] map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。
at(key) 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。
insert() 向 map 容器中插入键值对。
erase() 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。
swap() 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。
clear() 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。
emplace() 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。
emplace_hint() 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。
count(key) 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。

4. 插入操作

4.1 insert操作

无需指定插入位置,直接将键值对添加到 map 容器中。insert() 方法的语法格式有以下 2 种:

// 1、引用传递一个键值对
pair<iterator,bool> insert (const value_type& val);
// 2、以右值引用的方式传递键值对
template <class P>
pair<iterator,bool> insert (P&& val);

其中,val 参数表示键值对变量,同时该方法会返回一个 pair 对象,其中 pair.first 表示一个迭代器,pair.second 为一个 bool 类型变量:

  • 如果成功插入 val,则该迭代器指向新插入的 val,bool 值为 true;

  • 如果插入 val 失败,则表明当前 map 容器中存有和 val 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool 值为 false。

#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;
int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
   
    // 创建一个接收 insert() 方法返回值的 pair 对象
    std::pair<std::map<string, string>::iterator, bool> ret;
   
    // 插入 STL,由于 STL 并不是临时变量,因此会以第一种方式传参
    ret = mymap.insert(STL);
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    // 以右值引用的方式传递临时的键值对变量
    ret = mymap.insert({ "C语言教程","http://c.biancheng.net/c/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    // 插入失败样例
    ret = mymap.insert({ "STL教程","http://c.biancheng.net/java/" });
    cout << "ret.iter = <{" << ret.first->first << ", " << ret.first->second << "}, " << ret.second << ">" << endl;
    return 0;
}

创建临时的键值对变量,它们是等价的:

// 调用 pair 类模板的构造函数
ret = mymap.insert(pair<string,string>{ "C语言教程","http://c.biancheng.net/c/" });
// 调用 make_pair() 函数
ret = mymap.insert(make_pair("C语言教程", "http://c.biancheng.net/c/"));

insert() 方法还支持向 map 容器的指定位置插入新键值对,该方法的语法格式如下:

// 以普通引用的方式传递 val 参数
iterator insert (const_iterator position, const value_type& val);
// 以右值引用的方式传递 val 键值对参数
template <class P>
iterator insert (const_iterator position, P&& val);

其中 val 为要插入的键值对变量。注意,和第 1 种方式的语法格式不同,这里 insert() 方法返回的是迭代器,而不再是 pair 对象:

  • 如果插入成功,insert() 方法会返回一个指向 map 容器中已插入键值对的迭代器;

  • 如果插入失败,insert() 方法同样会返回一个迭代器,该迭代器指向 map 容器中和 val 具有相同键的那个键值对。

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

using namespace std;

int main()
{
    // 创建一个空 map 容器
    std::map<string, string> mymap;
   
    // 创建一个真实存在的键值对变量
    std::pair<string, string> STL = { "STL教程","http://c.biancheng.net/stl/" };
    // 指定要插入的位置
    std::map<string, string>::iterator it = mymap.begin();
    // 向 it 位置以普通引用的方式插入 STL
    auto iter1 = mymap.insert(it, STL);
    cout << iter1->first << " " << iter1->second << endl;
    // 向 it 位置以右值引用的方式插入临时键值对
    auto iter2 = mymap.insert(it, std::pair<string, string>("C语言教程", "http://c.biancheng.net/c/"));
    cout << iter2->first << " " << iter2->second << endl;
    // 插入失败样例
    auto iter3 = mymap.insert(it, std::pair<string, string>("STL教程", "http://c.biancheng.net/java/"));
    cout << iter3->first << " " << iter3->second << endl;
    return 0;
}

再次强调,即便指定了新键值对的插入位置,map 容器仍会对存储的键值对进行排序。也可以说,决定新插入键值对位于 map 容器中位置的,不是 insert() 方法中传入的迭代器,而是新键值对中键的值。

insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:

template <class InputIterator>
void insert (InputIterator first, InputIterator last);

其中 first 和 last 都是迭代器,它们的组合<first,last>可以表示某 map 容器中的指定区域。

4.2 operator[ ]

当实现“向 map [容器]中添加新键值对元素”的操作时,insert() 成员方法的执行效率更高;而在实现“更新 map 容器指定键值对的值”的操作时,operator[ ] 的效率更高。

向map容器中增添元素,insert()效率更高。

使用 operator[ ] 添加新键值对元素的流程是,先构造一个有默认值的键值对,然后再为其 value 赋值。那么,为什么不直接构造一个要添加的键值对元素呢,比如:

mymap.insert(mstr::value_type("C++", "脱发严重"));

此行代码和上面程序的执行效果完全相同,但它省略了创建临时 string 对象的过程以及析构该对象的过程同时还省略了调用 string 类重载的赋值运算符。由于可见,同样是完成向 map 容器添加新键值对,insert() 方法比 operator[ ] 的执行效率更高。

更新map容器中的键值对,operator[]效率更高。

insert() 方法在进行更新操作之前,需要有一个 pair 类型(也就是 map::value_type 类型)元素做参数。这意味着,该方法要多构造一个 pair 对象(附带要构造 2 个 string 对象),并且事后还要析构此 pair 对象(附带 2 个 string 对象的析构)。而和 insert() 方法相比,operator[ ] 就不需要使用 pair 对象,自然不需要构造(并析构)任何 pair 对象或者 string 对象。因此,对于更新已经存储在 map 容器中键值对的值,应优先使用 operator[ ] 方法。

 

标签:map,迭代,insert,容器,键值,pair
From: https://www.cnblogs.com/love-9/p/18152257

相关文章

  • Map使用对象作key
    Map介绍:Map是一个集合,一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以是任意类型的对象。Map中不能有重复的键(Key),每个键(key)都有一个对应的值(value);Map的key和value都是泛型的,所以Map的key和value可以任意类型的使用对象做Map的key......
  • multiset容器
    和set容器不同的是,multiset容器可以存储多个值相同的元素。multiset容器类模板的定义如下所示:template<classT,//存储元素的类型classCompare=less<T>,//指定容器内部的排序规则classAlloc=allocator<T......
  • Spring 源码阅读(二)IoC 容器初始化以及 BeanFactory 创建和 BeanDefinition 加载过程
    相关代码提交记录:https://github.com/linweiwang/spring-framework-5.3.33IoC容器三种启动方式XMLJavaSE:ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml")ApplicationContextcontext=newFileSystemXmlApplicationContext("C:/beans......
  • 容器结构分类
    容器结构分类容器在内存的布局方式红黑树高度平衡二分树,树的左边和右边会自己调整高度,避免查找的时候找到某一种很长的线一直找下去set和map都有红黑树实现HashTable链表过长链表的查找要一个一个查找.链表过程会导致速度过慢设置每一个链表不能过长如果过长就会通......
  • 容器使用之multiset
    容器使用之multiset可以理解为小型关联数据库底层结构:红黑树示例代码:#pragma#ifndef__MULTISET__#define__MMULTISE__​#include<set>#include<iostream>​usingnamespacestd;​namespaceMyTestSet{voidtest_set(long&value){multiset<string>c;/......
  • MIT6824 MapReduce总结
    MapReduce是一个分布式大任务计算框架,旨在可以方便Google内部的将大型任务拆分到集群环境下,以得到并行化的处理速度。在分布式情况下,多台机器协作完成一个大型任务需要考虑很多问题:整个分布式系统中都有哪些角色?可以预见的就是肯定有任务的拆分者负责拆分调度任务,有任务的实际......
  • set容器
    set容器定义于<set>头文件,并位于std命名空间中。因此如果想在程序中使用set容器,该程序代码应先包含如下语句:#include<set>usingnamespacestd;set容器的类模板定义如下:template<classT,//键key和值value的类型classCom......
  • Python数据容器
    1.容器定义:容纳多份数据的数据类型。Python的数据容器可以理解为C++中的数据结构,这些数据结构的方法多为“增删改查”。容器类型:列表、元组、字符串、2.列表list列表可理解为数组,下标从0开始。定义定义代码name_list=['zhangsan','lisi','wangwu']#字......
  • deque容器
    deque和vector的最大差异一在于deque允许常数时间内对头端或尾端进行元素的插入或移除操作。二在于deque没有所谓的容量概念,因为它是动态地以分段连续空间组合而成随时可以增加一块新的空间并拼接起来。虽然deque也提供随机访问的迭代器,但它的迭代器和vector、list容器的......
  • sqlmap简单使用方法
    基本格式sqlmap-u"http://www.vuln.cn/post.php?id=1"默认使用level1检测全部数据库类型sqlmap-u"http://www.vuln.cn/post.php?id=1" --dbmsmysql--level3指定数据库类型为mysql,级别为3(共5级,级别越高,检测越全面)cookie注入当程序有防get注入的时候,可以使用cookie注......