【C/C++】详解关联容器map的使用

🔗 运行环境:Matlab

🚩 撰写作者:左手の明天

🥇 精选专栏:《python》

🔥  推荐专栏:《算法研究》

🔐#### 防伪水印——左手の明天 ####🔐

💗 大家好🤗🤗🤗,我是左手の明天!好久不见💗

💗今天分享C/C++——关联容器map的使用💗

📆  最近更新:2024 年 05 月 26 日,左手の明天的第 331 篇原创博客

📚 更新于专栏:C/C++入门与进阶

🔐#### 防伪水印——左手の明天 ####🔐


一、引言

关联容器map是STL(Standard Template Library,标准模板库)中的一个重要组件,它提供了一对一的数据处理能力。在map中,每个元素都是一个关键字-值(key-value)对,其中关键字起到索引的作用,而值则表示与索引相关联的数据。这种特性使得map在处理一对一数据时,能够在编程上提供快速通道。

map内部自建一棵红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,因此map内部的所有数据都是有序的。这种有序性为数据的查找、插入和删除等操作提供了便利。

在map中,关键字是唯一的,每个关键字在map中只能出现一次。而值是可以修改的,可以通过关键字来访问和修改与之对应的值。此外,map还支持下标访问符,通过在[]中放入关键字,可以找到与该关键字对应的值。

总的来说,关联容器map是一个功能强大的数据结构,它能够在处理一对一数据时提供高效的数据处理能力,并在编程中提供便捷的操作方式。


二、Map的构造函数

map的构造函数有多种形式,包括匿名对象构造、有名构造、多参数构造函数的隐式类型转换等。在插入数据时,可以使用insert函数插入pair数据或value_type数据,也可以使用数组方式插入数据。需要注意的是,当使用insert函数插入数据时,如果map中已存在相同的关键字,则插入操作不会生效;而使用数组方式插入数据时,如果关键字已存在,则会覆盖以前该关键字对应的值。

std::map在C++标准库中有多个构造函数,允许你以不同的方式初始化map对象。以下是一些常见的std::map构造函数及其用法:

2.1 默认构造函数

默认构造函数创建一个空的map

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

2.2 复制构造函数

复制构造函数允许你从一个已存在的map对象创建一个新的map对象。

std::map<int, std::string> myMap1;
myMap1[1] = "one";
myMap1[2] = "two";std::map<int, std::string> myMap2(myMap1); // 使用myMap1初始化myMap2

2.3 初始化列表构造函数

使用初始化列表可以直接在构造函数中初始化map的元素。

std::map<int, std::string> myMap = {{1, "one"},{2, "two"},{3, "three"}
};

或者C++11及以后的语法:

std::map<int, std::string> myMap{{1, "one"},{2, "two"},{3, "three"}
};

2.4 迭代器范围的构造函数

你可以使用一对迭代器来从一个已存在的容器(如数组、另一个mapvector等)初始化map

std::pair<int, std::string> array[] = {{1, "one"},{2, "two"},{3, "three"}
};std::map<int, std::string> myMap(array, array + sizeof(array) / sizeof(array[0]));

2.5 分配器构造函数

分配器构造函数允许你使用一个自定义的分配器来管理map的内存。这在需要控制内存分配和释放时特别有用。

std::allocator<std::pair<const int, std::string>> alloc;
std::map<int, std::string> myMap(alloc);

在实际使用中,你通常会使用默认构造函数或初始化列表构造函数来创建和初始化map对象。例如,当你需要快速创建一个具有固定键值对的map时,初始化列表构造函数非常方便。

记住,由于map是有序的,所以当你使用初始化列表或迭代器范围构造函数时,键值对的顺序将影响最终map中元素的顺序。如果你使用默认的比较函数(std::less<Key>),元素将按照键的升序排列。如果你提供了自定义的比较函数,元素将根据该比较函数的逻辑排序。


三、Map的基本操作函数

C++ Maps是一种关联式容器,包含“关键字/值”对,如下表:

操作函数含义
begin()返回指向map头部的迭代器
clear()删除所有元素
count()返回指定元素出现的次数
empty()如果map为空则返回true
end()返回指向map末尾的迭代器
equal_range()返回特殊条目的迭代器对
erase()删除一个元素
find()查找一个元素
get_allocator()返回map的配置器
insert()插入元素
key_comp()返回比较元素key的函数

lower_bound()

返回键值>=给定元素的第一个位置
max_size()返回可以容纳的最大元素个数
rbegin()返回一个指向map尾部的逆向迭代器
rend()返回一个指向map头部的逆向迭代器
size()返回map中元素的个数

swap()

交换两个map

value_comp()返回比较元素value的函数
upper_bound()返回键值>给定元素的第一个位置

四、Map的实现

Map是存储键值对的数据结构,它提供了一种方便的方法来访问和操作这些键值对。在C语言中,可以使用哈希表来实现Map。Map提供了以下基本操作:

  1. 插入:将一个新的键值对插入到Map中。
  2. 查找:根据键查找对应的值。
  3. 删除:删除指定的键值对。
  4. 更新:修改指定键的值。
  5. 大小:返回Map中存储的键值对的数量。
  6. 遍历:遍历Map中的所有键值对。

在C++中,std::map是一个关联容器,它存储的元素都是键值对(key-value),并且根据键的值自动排序。每个键在map中只能出现一次,键不能修改,但对应的值可以修改。map的底层结构通常是用红黑树实现的,因此map中的元素总是有序的。

下面是使用std::map的基本步骤:

4.1 包含头文件

首先,你需要包含<map>头文件来使用std::map

#include <map>

4.2 定义和创建map对象

你可以使用std::map模板来定义和创建map对象。你需要指定键和值的类型。

std::map<int, std::string> myMap;  // 定义一个map,键是int类型,值是std::string类型

4.3 插入元素

你可以使用insert()函数来插入元素。通常,你可以使用std::make_pair来创建一个键值对,并将其插入到map中。

myMap.insert(std::make_pair(1, "one"));
myMap.insert(std::make_pair(2, "two"));

或者,你可以直接使用[]运算符来插入或修改元素(如果键已存在,则修改对应的值;如果键不存在,则插入新的键值对)。

myMap[3] = "three"; // 插入键为3,值为"three"的键值对

4.4 访问元素

你可以使用[]运算符或at()函数来访问元素。如果键不存在于map中,at()会抛出异常,而[]会创建一个新的元素。

std::string value = myMap[1]; // 访问键为1的元素的值
std::string value2 = myMap.at(2); // 另一种访问元素的方式

4.5 遍历map

你可以使用迭代器来遍历map中的所有元素。

for (std::map<int, std::string>::iterator it = myMap.begin(); it != myMap.end(); ++it) {std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}

或者使用基于范围的for循环(C++11及以后版本):

for (const auto& kv : myMap) {std::cout << "Key: " << kv.first << ", Value: " << kv.second << std::endl;
}

4.6 删除元素

你可以使用erase()函数来删除元素。

myMap.erase(1); // 删除键为1的元素

4.7 查找元素

你可以使用find()函数来检查元素是否存在。find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。

auto it = myMap.find(1);
if (it != myMap.end()) {std::cout << "Element found with key 1." << std::endl;
} 
else {std::cout << "Element not found." << std::endl;
}

这些是std::map的基本用法。在实际编程中,你可能还需要了解更多关于map的高级用法,比如自定义比较函数、使用emplace()函数插入元素等。你可以查阅C++标准库文档或相关教程来获取更多信息。

4.8 map中swap的用法:

Map中的swap不是一个容器中的元素交换,而是两个容器交换;

#include <map>
#include <iostream>using namespace std;int main( )
{map <int, int> m1, m2, m3;map <int, int>::iterator m1_Iter;m1.insert ( pair <int, int> ( 1, 10 ) );m1.insert ( pair <int, int> ( 2, 20 ) );m1.insert ( pair <int, int> ( 3, 30 ) );m2.insert ( pair <int, int> ( 10, 100 ) );m2.insert ( pair <int, int> ( 20, 200 ) );m3.insert ( pair <int, int> ( 30, 300 ) );cout << "The original map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter->second;cout << "." << endl;// This is the member function version of swap//m2 is said to be the argument map; m1 the target mapm1.swap( m2 );cout << "After swapping with m2, map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;cout << "After swapping with m2, map m2 is:";for ( m1_Iter = m2.begin( ); m1_Iter != m2.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;// This is the specialized template version of swapswap( m1, m3 );cout << "After swapping with m3, map m1 is:";for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << " " << m1_Iter -> second;cout << "." << endl;
}

4.9 map的sort问题:

Map中的元素是自动按key升序排序,所以不能对map用sort函数:

#include <map>
#include <iostream>using namespace std;int main( )
{map <int, int> m1;map <int, int>::iterator m1_Iter;m1.insert ( pair <int, int> ( 1, 20 ) );m1.insert ( pair <int, int> ( 4, 40 ) );m1.insert ( pair <int, int> ( 3, 60 ) );m1.insert ( pair <int, int> ( 2, 50 ) );m1.insert ( pair <int, int> ( 6, 40 ) );m1.insert ( pair <int, int> ( 7, 30 ) );cout << "The original map m1 is:"<<endl;for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ )cout << m1_Iter->first<<" "<<m1_Iter->second<<endl;
}

The original map m1 is:

1 20

2 50

3 60

4 40

6 40

7 30


五、Map定义自定义比较函数

std::map中定义自定义比较函数是通过向std::map模板的第三个参数传递一个比较对象来实现的。这个比较对象通常是一个函数对象(也称为仿函数)或者Lambda表达式,它需要重载operator()来定义两个元素之间的比较逻辑。

下面是使用自定义比较函数的std::map的几种方式:

5.1 使用函数对象(仿函数)

首先,定义一个比较函数对象:

struct MyComparator {bool operator()(const int& a, const int& b) const {// 定义比较逻辑,这里假设我们要按照降序排列return a > b;}
};

然后,使用这个比较函数对象来创建std::map

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

5.2 使用Lambda表达式(C++11及以后)

在C++11及更高版本中,你可以使用Lambda表达式作为比较函数:

auto comp = [](const int& a, const int& b) {// 定义比较逻辑return a > b;
};std::map<int, std::string, decltype(comp)> myMap(comp);

注意这里使用了decltype(comp)来自动推断比较函数的类型。

5.3 使用标准库函数对象

有时,你可能不需要完全自定义比较逻辑,而是想要对map中的键进行某种转换后再比较。在这种情况下,你可以使用标准库提供的函数对象适配器,如std::greater<>std::less<>,配合自定义的键转换函数。但是,这通常是通过自定义比较函数对象来实现的,其中内部使用这个转换函数。

示例:降序排列的std::map

假设想要一个按照键的降序排列的std::map

#include <iostream>
#include <map>
#include <string>int main() {// 使用自定义比较函数对象创建降序排列的mapstd::map<int, std::string, std::greater<int>> myMap;myMap[3] = "three";myMap[1] = "one";myMap[2] = "two";// 遍历map,显示键值对for (const auto& kv : myMap) {std::cout << "Key: " << kv.first << ", Value: " << kv.second << std::endl;}return 0;
}

在这个例子中,使用了std::greater<int>作为比较函数对象,它会使std::map按照键的降序排列。

记住,std::map的默认比较函数是std::less<Key>,它会产生升序排列的map。通过提供自定义的比较函数对象,你可以改变元素的排序方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/16134.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mpv常用快捷键

1 mpv mpv是Linux下的一个开源视频播放器&#xff0c;使用Manjaro的话安装方式如下&#xff1a; paru -S mpv2 常用快捷键 q&#xff1a;推出w/e&#xff1a;视频缩放r/t&#xff1a;调整字幕位置u&#xff1a;开启/关闭ass/ssa字幕覆盖i&#xff1a;显示当前播放的视频信息…

Oracle 并行和 session 数量的

这也就是为什么我们指定parallel为4&#xff0c;而实际并行度为8的原因。 insert create index&#xff0c;发现并行数都是加倍的 Indexes seem always created with parallel degree 1 during import as seen from a sqlfile. The sql file shows content like: CREATE INDE…

求平方数 1 到 N 之间所有正整数的平方数

概念&#xff1a; 平方数的概念&#xff1a; 平方数是指一个数的平方等于另一个数的数&#xff0c;具有正平方数和负平方数&#xff0c;其性质和运用在多领域中具有重要意义&#xff0c;如几何、自然科学、计算机科学和物理学。平方数的计算和运用在多领域中常见&#xff0c;例…

滑不动窗口的秘密—— “滑动窗口“算法 (Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

《python编程从入门到实践》day39

# 昨日知识点回顾 创建主页、继承模版、显示特定主题页面 # view.py from django.shortcuts import render# 导入所需数据相关联的模型 from .models import Topic# Create your views here. def index(request):"""学习笔记的主页"""#…

Java进阶学习笔记13——抽象类

认识抽象类&#xff1a; 当我们在做子类共性功能抽取的时候&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了。在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff0c;而类中如果有抽象方法&#xff0c;该类就定义为抽象类…

ISCC2024个人挑战赛WP-迷失之门

&#xff08;非官方解&#xff0c;以下内容均互联网收集的信息和个人思路&#xff0c;仅供学习参考&#xff09; 迷失之门 方法一&#xff1a; IDA看一下 check函数逻辑 进入到check2函数 R键将ascii码转字符&#xff0c;写出逆向脚本 #include <stdio.h> #include &l…

嵌入式0基础开始学习 Ⅱ 数据结构(7)小结练习

1,如果使用比较高效的算法判断单链表有没有环的算法中&#xff0c;至少需要几个指针&#xff1f; A,1 B,2 C,3 D,4 2&#xff0c;以链接方式存储的线性表(X1,X2,...,Xn),当访问第i个元素的时间复杂度为? A,o(1) B,o(n) C,o(logn) Do(n) 3,下列链表中&…

Linux C++ Socket 套接字、select、poll、epoll 实例

文章目录 1. 概述2. TCP 网络编程实例2.1 服务器端2.2 客户端2.3 运行截图 3. I/O 模型3.1 阻塞式I/O模型3.2 非阻塞I/O模型3.3 I/O 复用模型3.4 信号驱动式I/O3.5 异步I/O模型 4. I/O复用之 select4.1 select 函数描述4.2 服务端代码4.3 客户端代码4.4 运行截图 5. I/O复用之 …

RocketMq局部顺序消息

package com.ldj.rocketmq.producer;import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message;import java.nio.charset.StandardCharsets;/*** User: ldj* Date: 2024/5/26* Time: 15:09* Description: 局部顺序消…

【Linux】$()中的内容与不加$()时有什么区别

$()中的内容与不加$()有什么区别&#xff0c;例如$(/usr/local/hadoop/bin/hadoop classpath)与/usr/local/hadoop/bin/hadoop classpath两者有何区别&#xff1f;&#xff1f;&#xff1f; 关于这个问题&#xff0c;笔者建议可以参考如下文章&#xff1a; Linux—shell中$((…

css卡片翻转 父元素翻转子元素不翻转效果

css卡片翻转 父元素翻转子元素不翻转效果 vue <div class"moduleBox"><div class"headTitle"><span class"headName">大额案例</span></div><div class"moduleItem"><span class"module…

three.js判断物体在人的前面,还是后面

three.js判断物体在人的前面&#xff0c;还是后面 const player new THREE.Vectors(10, 0, 5); const mesh new THREE.Vectors(15, 0, 6);上面&#xff0c;两个变量分别表示&#xff0c;玩家的位置&#xff0c;物体的位置。 从这发现&#xff0c;当玩家和物体的角度关系 小…

spring boot 整合j2cache 项目启动警告 Redis mode [null] not defined. Using ‘single‘

好 之前的文章 spring boot 整合j2cache 基础操作 在spring boot环境中整合了 j2cache 我们 项目启动时 日志会有一个关键信息 Redis的模式 没有定义 默认使用 single Redis 的这个模式有四种 大家可以自己去网上找一下 做个了解 不用很纠结 我们直接在 j2cache.properties …

一文读懂Apollo客户端配置加载流程

本文基于 apollo-client 2.1.0 版本源码进行分析 Apollo 是携程开源的配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性。 Apollo支持4个维度管理Key-Value格式的配…

Elasticsearch智能数据分析平台项目

Elasticsearch智能数据分析平台项目是一个功能强大且灵活的数据分析工具,旨在帮助企业快速、准确地分析和挖掘数据中的价值。以下是关于该项目的一些关键特点和功能: 数据搜索: Elasticsearch作为全球下载量最大的搜索引擎,支持从关键字搜索到向量搜索等多样化搜索方式,让…

比勤奋更重要的是系统思考的能力

不要在接近你问题症状的地方寻找解决办法&#xff0c;要追溯过去&#xff0c;查找问题的根源。通常&#xff0c;最有效的活动是最微妙的。有时最好按兵不动&#xff0c;使系统自我修正&#xff0c;或让系统引导行动。有时会发现&#xff0c;最好的解决办法出现在完全出乎预料的…

HTML蓝色爱心

目录 写在前面 HTML入门 完整代码 代码分析 运行结果 系列推荐 写在后面 写在前面 最近好冷吖&#xff0c;小编给大家准备了一个超级炫酷的爱心&#xff0c;一起来看看吧&#xff01; HTML入门 HTML全称为HyperText Markup Language&#xff0c;是一种标记语言&#…

C++-指针

在C中&#xff0c;指针是至关重要的组成部分。它是C语言最强大的功能之一&#xff0c;也是最棘手的功能之一。 指针具有强大的能力&#xff0c;其本质是协助程序员完成内存的直接操纵。 指针&#xff1a;特定类型数据在内存中的存储地址&#xff0c;即内存地址。 指针变量的定…

2024.5组队学习——MetaGPT(0.8.1)智能体理论与实战(下):多智能体开发

传送门&#xff1a; 《2024.5组队学习——MetaGPT&#xff08;0.8.1&#xff09;智能体理论与实战&#xff08;上&#xff09;&#xff1a;MetaGPT安装、单智能体开发》《2024.5组队学习——MetaGPT&#xff08;0.8.1&#xff09;智能体理论与实战&#xff08;中&#xff09;&…