【c++丨STL】map/multimap的使用

🌟🌟作者主页:ephemerals__****
🌟🌟所属专栏:C++STL

目录

前言

一、map/multimap的介绍

二、map的默认成员函数

构造函数constructor

析构函数destructor

赋值重载

三、map的迭代器接口

四、map的容量相关接口

empty

size

五、map的元素访问接口

[operator[ ]](#operator[ ])

at

六、map的增删相关接口

insert

erase

swap

clear

七、map的其他操作接口

find

count

[lower_bound、 upper_bound 和 equal_range](#lower_bound、 upper_bound 和 equal_range)

八、map的具体使用

总结


前言

之前我们学习了STL关联式容器------set/multiset的使用,本篇文章我们将介绍另一组关联式容器map(映射表)/multimap(多重映射表)

一、map/multimap的介绍

与set相同,map的底层也是基于红黑树 实现的,其内部元素根据键自动升序排列

但两者有如下区别

存储内容 :set存储的是 ,而map存储的是键值对(数据元素是一个pair

元素访问 :set只能访问 ,而map可以通过键来访问对应的值,并且值可以支持修改

因此,set适用于唯一元素的集合操作,如去重;而map更适用于处理键值关系

相比map,multimap支持多个相同键存在。

map和multimap相关接口查阅:

<map> - C++ Reference

map和multimap的使用方法基本相同 ,但相比multimap,map更加常用,所以接下来的内容将主要聚焦于map常用接口的使用方法 ,而在与multimap出现显著区别时,我们会适当提及它。

注意:在使用map/multimap时,要引头文件<map>,且该容器定义在命名空间std当中。

二、map的默认成员函数

构造函数constructor

map有五种构造函数,其中较为常用的有如下四个:

|-----------------------------------------------|-------------------------|
| 函数原型 | 功能说明 |
| map(); | 无参构造 |
| map(InputIterator first, InputIterator last); | 迭代器区间构造 |
| map(const map& x); | 拷贝构造,用一个map对象构造另一个map对象 |
| map(initializer_list<value_type> il); | 初始化器构造 |

代码示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<string, int> m1;//无参构造,创建一个map对象,映射关系为string,int

	map<string, int> m2({ {"hello",1},{"hehe",3},{"haha",2} });//初始化器构造

	map<string, int> m3(m2);//拷贝构造

	map<string, int> m4(++m3.begin(), --m3.end());//迭代器区间构造
	return 0;
}

析构函数destructor

析构函数在对象生命周期结束时自动调用,会销毁容器中的所有元素,并使用其分配器释放map容器分配的所有存储空间。

赋值重载

将新内容赋值给容器,替换当前内容。

代码示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m1 = { {1,1},{2,2},{3,3} };
	map<int, int> m2;
	map<int, int> m3;

	m2 = m1;//对象赋值
	m3 = { {1,1},{2,2},{3,3} };//初始化器赋值
	return 0;
}

三、map的迭代器接口

map的迭代器接口使用方法与之前学过的容器都相同,这里就不多赘述。

需要注意以下几点:

1. map的迭代器是双向迭代器

2. map迭代器进行顺序遍历的结果是有序的,这归因于它采用了 红黑树的中序遍历 算法。

3. 无论是使用普通迭代器还是const迭代器,都无法修改map元素的键key,但是普通迭代器可以修改value

四、map的容量相关接口

empty

判断map容器是否为空,若为空,则返回true;否则返回false。

这个函数不会以任何方式修改容器。

代码示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	cout << m.empty() << endl;//判空
	return 0;
}

size

size返回map容器内的元素个数

代码示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	cout << m.size() << endl;
	return 0;
}

五、map的元素访问接口

由于map元素是以键值对的方式存储,所以其相比set多了元素访问接口,我们可以使用该接口通过键来访问值。两个接口的详细讲解如下:

operator[ ]

operator[]在map当中的功能十分强大,它不仅支持根据键访问值,还支持元素的插入

首先,对于该重载函数,我们应将键key作为下标来传参

如果容器内已经有相同的key,那么该函数返回value的引用,达到根据键访问或修改值的效果。

如果容器内不存在key,那么该函数就会在容器中插入一个带有该key的新元素,并返回value的引用。此时我们将其返回值进行修改,就达到了插入一个键值对的效果。注意:若我们没有修改其返回值,容器中的元素个数也会+1,此时value的值为默认构造值

调用这个函数等价于以下语句:

cpp 复制代码
(*((this->insert(make_pair(k,mapped_type()))).first)).second

使用示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };
	
	m[3] = 0;//将键3的映射值修改为0

	m[4] = 4;//插入一个键值对{4,4}

	return 0;
}

at

与operator[ ]不同,at仅仅支持根据键访问或修改值,但不支持元素插入。我们将key作为参数传入,返回值是valus的引用。

当容器内不存在相同的key时,该函数会抛出异常

使用示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };

	m.at(3) = 0;//将键3的映射值修改为0

	m.at(4) = 4;//找不到4,抛出异常
	return 0;
}

注意:对于multimap,不存在operator[ ]和at这两个函数。

六、map的增删相关接口

insert

insert的作用是插入元素,增加容器的大小。支持单个键值对插入、迭代器区间插入和初始化器插入。

因为map中元素的键是唯一的,所以插入操作会检查每个被插入元素的键是否与容器中已经存在的元素的键相等,如果相等,则不插入该元素,并返回包含重复键的元素的迭代器(如果该重载函数有返回值)

这里介绍一下第一个重载函数(单个元素插入)的返回值:是一个pair,当插入成功时,pair的第一个元素是指向新元素的迭代器,第二个元素是true;当因有重复键而插入失败时,pair的第一个元素是指向包含重复键的元素的迭代器,第二个元素是false。

使用示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m;
	map<int, int> m2({ {3,3},{4,4} });

	//单个元素插入
	m.insert({ 1,1 });
	m.insert(make_pair(2, 2));

	//迭代器区间插入
	m.insert(m2.begin(), m2.end());

	//初始化器插入
	m.insert({ {5,5},{6,6} });
	return 0;
}

在map当中,使用operator[ ]插入元素的方法会比insert更加常用。

对于multimap的insert函数,其插入方法与map相同。并且即使有重复键,也会插入成功。

erase

erase的作用是删除map中的元素包括迭代器指定删除、按键删除和迭代器区间删除

对于按键删除 的重载函数,它的返回值是**被成功删除的元素数量,**它的作用在支持重复键的multimap中较大。

使用示例:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	map<int, int> m = { {1,1},{2,2},{3,3} };

	m.erase(1);//按键删除

	m.erase(m.begin());//迭代器指定位置删除

	m.erase(m.begin(), m.end());//迭代器区间删除
	return 0;
}

swap

swap用于交换两个map容器的内容。使用方法与其他容器相同,不多赘述。

clear

清空map容器内的所有元素

七、map的其他操作接口

find

find用于按键查找元素如果找到了就返回指向该元素的迭代器,否则返回尾迭代器

count

count的作用是获取容器中键key所在元素的出现次数对于不允许键重复的map,它只返回0(表示不存在)或1(表示存在),可以用于判断某个元素是否在容器当中。而对于multimap,可以统计数量

lower_bound upper_bound equal_range

这三个函数的使用方法请参照这篇文章:

【c++丨STL】set/multiset的使用-CSDN博客

它们的使用方法、允许重复元素出现时的逻辑与set/multiset基本相同 ,并且并不是很常用,注意对于map/multimap而言是以键key为查找标准即可。

八、map的具体使用

接下来我们使用map来解决一个具体问题:有一个字符串数组,其中包含各种水果的名称,统计每种水果的出现次数

代码如下:

cpp 复制代码
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> m;//创建map,映射关系为<string,int>,键表示水果名称,值表示该水果出现次数

	//遍历字符串数组
	for (auto& str : arr)
	{
		m[str]++;//统计
	}

	//遍历map
	for (auto& kv : m)
	{
		cout << kv.first << ':' << kv.second << endl;
	}
	return 0;
}

运行结果:

我们巧妙地利用了operator[ ]函数,当遇到新水果时,插入{水果,0},然后将其自增,出现次数变为1;当遇到已有水果时,直接将出现次数进行累加。这样就达到了统计每种水果的出现次数的效果,最后遍历输出即可。

总结

本篇文章,我们讲解了STL中的另一对关联式容器map/multimap的使用方法与具体场景。之后博主会和大家一起,在实现红黑树的基础上尝试模拟实现set和map两种容器。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关推荐
贵州晓智信息科技4 分钟前
Three.js实现动态水泡效果逐步解析GLSL着色器
开发语言·javascript·着色器
智驾11 分钟前
C++,设计模式,【目录篇】
c++·设计模式
汤姆和杰瑞在瑞士吃糯米粑粑19 分钟前
【优先算法】滑动窗口--结合例题详解学习
开发语言·数据结构·c++·算法
轻口味38 分钟前
【HarmonyOS NAPI 深度探索10】HarmonyOS Next 中的 NAPI 的架构与原理
c++·华为·架构·harmonyos·napi·harmonyos-next
code_shenbing1 小时前
C# 控制打印机:从入门到实践
开发语言·c#
magic 2451 小时前
JVM体系结构
java·开发语言·jvm·intellij-idea·idea
我们的五年1 小时前
【C++课程学习】:C++中的IO流(istream,iostream,fstream,sstream)
linux·c++·学习
code_shenbing1 小时前
C# 解析 HTML 实战指南
开发语言·c#·html
蟹至之2 小时前
类和对象(3)——继承:extends关键字、super关键字、protected关键字、final关键字
java·开发语言·继承·类和对象
万亿少女的梦1682 小时前
基于PHP的校园兼职系统的设计与开发
开发语言·网络·数据库·爬虫·网络安全·php