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

简介: 本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。

前言

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

一、map/multimap的介绍

image.png

image.png

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

但两者有如下区别

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

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

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

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

map和multimap相关接口查阅:

- C++ Reference

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

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

二、map的默认成员函数

image.png

构造函数constructor

image.png

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

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

代码示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
   
    map<string, int> m1;
    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

image.png

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

赋值重载

image.png

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

代码示例:

#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的迭代器接口

image.png

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

    需要注意以下几点:

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

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

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

四、map的容量相关接口

image.png

empty

image.png

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

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

代码示例:

#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;
}

image.png

size

image.png

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

代码示例:

#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;
}

image.png

五、map的元素访问接口

image.png

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

operator[ ]

image.png

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

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

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

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

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

(*((this->insert(make_pair(k,mapped_type()))).first)).second

使用示例:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
   
    map<int, int> m = {
    {
   1,1},{
   2,2},{
   3,3} };
    m[3] = 0;
    m[4] = 4;
    return 0;
}

at

image.png

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

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

使用示例:

#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;
    m.at(4) = 4;
    return 0;
}

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

六、map的增删相关接口

image.png

insert

image.png

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

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

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

使用示例:

#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

image.png

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

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

使用示例:

#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

image.png

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

clear

image.png

清空map容器内的所有元素

七、map的其他操作接口

image.png

find

image.png

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

count

image.png

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

lower_bound** upper_bound equal_range**

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

https://developer.aliyun.com/article/1656951

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

八、map的具体使用

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

代码如下:

#include <map>
#include <string>
#include <iostream>
using namespace std;

int main()
{
   
    string arr[] = {
    "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
    map<string, int> m;
    for (auto& str : arr)
    {
   
        m[str]++;
    }
    for (auto& kv : m)
    {
   
        cout << kv.first << ':' << kv.second << endl;
    }
    return 0;
}

运行结果:

image.png

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

总结

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

相关文章
|
1月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
61 2
|
1月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
72 3
|
2月前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
110 1
|
29天前
使用 entrySet 遍历 Map 类集合 KV
使用 entrySet 遍历 Map 类集合 KV
|
10月前
|
Dart
Dart之集合详解(List、Set、Map)
Dart之集合详解(List、Set、Map)
|
7月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
7月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
8月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
8月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。
|
8月前
|
算法 Java 索引
【Java集合类面试四】、 描述一下Map put的过程
这篇文章详细描述了HashMap中put操作的过程,包括首次扩容、计算索引、插入数据以及链表转红黑树和可能的再次扩容。
【Java集合类面试四】、 描述一下Map put的过程