C++ 中 map 的实用操作指南

在 C++ 中,`map` 是一种基于键值对的容器,其中的数据总是以成对形式出现。如所示,每一对中的第一个元素是关键字(key),这些关键字在 `map` 中具有唯一性,即每个关键字只能出现一次;第二个元素是与该关键字相对应的值(value)。这种结构使得 `map` 成为管理具有明确关联关系的数据的理想选择。

 一. 声明

在下面的 C++ 代码片段中,我们首先包含了 map 容器所需的头文件 map,然后声明了两个 map 类型的变量:ID_Name。这些 map 存储的是基于 int 类型的关键字和 string 类型的值。

// 包含 map 容器的头文件
#include <map>
#include <string> // 确保 string 类型被正确引入// 声明 map 容器,存储整数 ID 和对应的名称
std::map<int, std::string> ID_Name;// 使用列表初始化(自 C++11 起支持)来初始化 map
// 注意:这需要 C++11 或更高版本的编译器支持
std::map<int, std::string> ID_Name = {{ 2015, "Jim" },{ 2016, "Tom" },{ 2017, "Bob" }
};

代码注释解释:

  • #include <map>:包含标准库中的 map 容器,它允许我们创建和操作键值对集合。
  • #include <string>:包含字符串类型,确保 string 类型可以在 map 中使用。
  • 第一个 map 初始化示例没有在声明时提供任何初始值。
  • 第二个 map 使用了 C++11 引入的列表初始化语法 {} 来直接在声明时初始化 map。这种初始化方式允许我们在声明 map 变量的同时,明确指定其包含的键值对。

注意:

  • 列表初始化语法 {} 是从 C++11 开始支持的。因此,如果你使用的是 C++11 之前的编译器版本(例如 Visual Studio 2012 或更早版本),这种初始化方式将导致编译错误。确保你的开发环境支持 C++11 或更高版本,以便使用这种便捷的初始化方式。

二. 插入操作

2.1 使用 [ ] 进行单个插入

        在 C++ 中,使用 map 容器的 [] 操作符可以便捷地进行插入或修改操作。例如:

#include <map>
#include <string>std::map<int, std::string> ID_Name;// 使用[]操作符进行插入或修改
// 如果键2015已存在,此操作将修改其对应的值为"Tom"
// 如果键2015不存在,将创建一个新键值对(2015, "Tom")
ID_Name[2015] = "Tom";
2.2 使用 insert 进行单个和多个插入

   std::map 提供了几种重载的 insert 方法,用于插入单个或多个元素:

#include <map>
#include <string>
#include <iterator>std::map<int, std::string> ID_Name;// 插入单个键值对,并返回一个包含迭代器和成功标志的pair
// 如果插入的键已存在,插入操作将失败,返回的pair中的bool为false
auto result = ID_Name.insert(std::make_pair(2015, "Tom"));
if (result.second) {std::cout << "Insert successful" << std::endl;
} else {std::cout << "Key already exists" << std::endl;
}// 在指定位置尝试插入,虽然位置提示可能提高效率,但插入位置不保证成功
auto pos = ID_Name.begin();
ID_Name.insert(pos, std::make_pair(2016, "Jim")); // 提示插入位置// 插入多个键值对(假设first和last是合法的迭代器)
// ID_Name.insert(first, last);// C++11起支持使用初始化列表插入多个键值对
ID_Name.insert({ {2017, "Bob"}, {2018, "Alice"} });

代码注释解释:

  • 使用 insert 插入单个键值对时,返回类型为 std::pair<std::map<int, std::string>::iterator, bool>bool 表示插入是否成功(true 表示成功,false 表示键已存在),而迭代器指向被插入或已存在的元素。

  • insert 的另一个重载接受一个“提示”位置,这有助于优化插入操作的性能。尽管如此,正确的插入位置仍由 map 决定。

  • 插入范围的重载接受一对迭代器,允许从另一个相容容器中批量插入元素。

  • 使用初始化列表插入多个键值对是 C++11 引入的功能,它允许直接在 insert 函数中指定一个键值对列表。

下面是具体使用示例:

#include <iostream>
#include <map>int main() {std::map<char, int> mymap;// 插入单个键值对mymap.insert(std::pair<char, int>('a', 100));mymap.insert(std::pair<char, int>('z', 200));// 尝试插入已存在的键,并获取操作的结果std::pair<std::map<char, int>::iterator, bool> ret;ret = mymap.insert(std::pair<char, int>('z', 500));if (!ret.second) {std::cout << "Element 'z' already existed with a value of " << ret.first->second << '\n';}// 在指定位置插入,尽管位置指定可能帮助提高插入效率,但最终位置仍由 map 控制std::map<char, int>::iterator it = mymap.begin();mymap.insert(it, std::pair<char, int>('b', 300));  // 提示位置接近正确,有助于提高效率mymap.insert(it, std::pair<char, int>('c', 400));  // 提示位置较远,效率提升可能不明显// 从一个 map 插入另一个 map 的部分数据std::map<char, int> anothermap;anothermap.insert(mymap.begin(), mymap.find('c')); // 插入从开始到 'c' 之前的元素// 使用初始化列表批量插入多个键值对anothermap.insert({ {'d', 100}, {'e', 200} });return 0;
}
  • 单个插入:通过 insert 方法插入单个键值对。如果尝试插入的键已存在,则 insert 不会修改已有键值对,而是返回一个指向该元素的迭代器和一个表示插入失败的 bool 值 (false).

  • 指定位置插入insert 的这个重载版本接受一个迭代器作为“位置提示”。这个提示可以帮助 map 更高效地插入新元素,尤其是当提示位置接近目标位置时。但请注意,map 总是确保元素的正确排序,因此最终插入位置可能与提示位置不同。

  • 范围插入:这种形式的 insert 允许从另一个 map 或同类型容器的一部分进行数据插入。在本例中,我们从 mymap 的开始到找到的 'c' 之前的元素进行插入。

  • 列表插入:C++11 引入了初始化列表,您可以使用它来简洁地插入多个键值对。这种方式非常适合在构造时或在需要添加多个元素时使用。

三. 取值操作

在 C++ 的 std::map 容器中,元素的取值主要通过两种方式实现:at() 和操作符 []。两者的主要区别在于对键的存在性的检查。

#include <iostream>
#include <map>
#include <string>int main() {std::map<int, std::string> ID_Name;// 使用[]操作符,如果键2016不存在,会自动创建该键并初始化其值(空字符串)// 因此,下面的语句不会报错,但打印出的结果是空字符串std::cout << ID_Name[2016] << std::endl;// 使用at()方法会进行键的存在性检查,如果键不存在则抛出std::out_of_range异常try {std::string name = ID_Name.at(2016); // 这里会抛出异常} catch (const std::out_of_range& e) {std::cerr << "Error: " << e.what() << std::endl;}return 0;
}

四. 容量查询

std::map 提供了几个方法来查询其容量和大小,这些方法有助于了解容器的状态和性能调优。

#include <iostream>
#include <map>int main() {std::map<int, std::string> ID_Name;// 查询 map 是否为空bool isEmpty = ID_Name.empty();std::cout << "Map is " << (isEmpty ? "empty" : "not empty") << std::endl;// 查询 map 中键值对的数量size_t numElements = ID_Name.size();std::cout << "Map size: " << numElements << std::endl;// 查询 map 所能包含的最大键值对数量size_t maxCapacity = ID_Name.max_size();std::cout << "Map maximum size: " << maxCapacity << std::endl;// 查询关键字为 key 的元素的个数,在 map 中结果非 0 即 1size_t countKey = ID_Name.count(2016);std::cout << "Count for key 2016: " << countKey << std::endl;return 0;
}

注释解释:

  • empty() 返回一个布尔值,表示 map 是否为空。
  • size() 返回 map 中存储的键值对的数量。
  • max_size() 返回 map 可以存储的最大键值对数量。这个值受限于系统或库的实现,通常非常大,实际可达到的大小取决于系统资源。
  • count(const Key& key) 检查特定键在 map 中的存在性,返回 0 或 1,因为 map 的键是唯一的。

五. 迭代器

在 C++ 中,std::map 提供了多种方式来获取迭代器,用以访问或遍历容器中的元素。这里我们将详细解释这些迭代器的获取方式及其用途。

获取迭代器的函数

对于 std::map,有以下几种函数用于获取迭代器:

  • 正向迭代器

    • begin() / end(): 这两个函数返回的迭代器分别指向容器中的第一个元素和末尾(末尾即容器最后一个元素的下一个位置,不是最后一个元素)。如果容器是非常量的,这些迭代器允许修改它们所指向的元素的值。
    • cbegin() / cend(): 这对函数总是返回常量迭代器,即使在非常量的 map 上调用也是如此。常量迭代器不允许修改其指向的元素,这主要用于只读访问。
  • 反向迭代器

    • rbegin() / rend(): 这是反向迭代器的版本,rbegin() 指向最后一个元素,而 rend() 指向第一个元素之前的位置。它们允许你从后向前遍历 map
    • crbegin() / crend(): 这对函数返回的是常量反向迭代器,不允许修改指向的元素,适用于只读逆序遍历。

        以下是一个示例代码,展示如何使用这些迭代器:

#include <iostream>
#include <map>int main() {std::map<int, int> mmap = {{1, 100}, {2, 200}, {3, 300}};const std::map<int, int> const_mmap = mmap;// 获取迭代器auto it = mmap.begin(); // 非常量迭代器,可以修改值auto cit = mmap.cbegin(); // 常量迭代器,只读// 对于常量 map,所有迭代器均为常量迭代器auto const_it = const_mmap.begin(); // 常量迭代器,只读auto const_cit = const_mmap.cbegin(); // 常量迭代器,只读// 输出std::cout << "First element value using non-const iterator: " << it->second << std::endl;std::cout << "First element value using const iterator from non-const map: " << cit->second << std::endl;std::cout << "First element value using const iterator from const map: " << const_it->second << std::endl;return 0;
}

注意事项

  • 如果 map 是空的,则 begin() 和 end() 返回的迭代器相等,同样适用于 rbegin() 和 rend()
  • 迭代器操作通常不涉及加减整数值(这是针对连续内存的 vector 等容器的操作),但可以进行递增 (++) 或递减 (--) 来访问相邻元素。

六. 删除和交换

6.1 删除

std::map 提供了几种删除元素的方法:

  • 单个元素删除
    • erase(iterator pos): 删除迭代器 pos 指向的元素,并返回指向下一个元素的迭代器。
  • 区间删除
    • erase(const_iterator first, const_iterator last): 删除从 first 到 last(不包括 last)的元素,并返回指向下一元素的迭代器。
  • 按键删除
    • erase(const key_type& key): 根据键值 key 删除元素。由于 map 中的键值是唯一的,所以这个方法要么删除一个元素,要么一个都不删除(如果键不存在)。返回删除的元素数量,即 0 或 1。
  • 清空整个 map
    • clear(): 删除 map 中的所有元素,使其大小变为 0。
#include <iostream>
#include <map>int main() {std::map<char, int> mymap;// 插入一些元素mymap['a'] = 10;mymap['b'] = 20;mymap['c'] = 30;mymap['d'] = 40;// 使用迭代器删除单个元素auto it = mymap.find('b');if (it != mymap.end()) {mymap.erase(it);}// 输出当前 mapstd::cout << "Map after erasing 'b':" << std::endl;for (const auto& p : mymap) {std::cout << p.first << " => " << p.second << std::endl;}// 使用键值直接删除元素mymap.erase('c');// 输出当前 mapstd::cout << "Map after erasing 'c':" << std::endl;for (const auto& p : mymap) {std::cout << p.first << " => " << p.second << std::endl;}// 清空整个 mapmymap.clear();std::cout << "Map size after clearing: " << mymap.size() << std::endl;return 0;
}

6.2 交换
  • swap(map& other): 与另一个 map other 交换内容。这通常是一个非常快速的操作,因为只涉及到内部数据结构的指针交换,而不是元素本身的复制。
#include <iostream>
#include <map>int main() {std::map<char, int> map1, map2;// 初始化 map1 和 map2map1['x'] = 100;map1['y'] = 200;map2['a'] = 10;map2['b'] = 20;// 输出原始 mapstd::cout << "Original map1:" << std::endl;for (const auto& p : map1) {std::cout << p.first << " => " << p.second << std::endl;}std::cout << "Original map2:" << std::endl;for (const auto& p : map2) {std::cout << p.first << " => " << p.second << std::endl;}// 交换 map1 和 map2map1.swap(map2);// 输出交换后的 mapstd::cout << "Map1 after swap:" << std::endl;for (const auto& p : map1) {std::cout << p.first << " => " << p.second << std::endl;}std::cout << "Map2 after swap:" << std::endl;for (const auto& p : map2) {std::cout << p.first << " => " << p.second << std::endl;}return 0;
}

七. 顺序比较

每个 std::map 都有一个内部的比较对象,用于维持键的顺序。你可以通过 key_comp() 方法获取这个比较对象。

  • key_comp()
    • 返回一个用于比较键的函数对象。如果第一个参数在顺序上位于第二个参数之前,则此函数对象返回 true
std::map<char, int> mymap;
auto mycomp = mymap.key_comp();mymap['a'] = 100;
mymap['b'] = 200;bool result = mycomp('a', 'b'); // 返回 true,因为 'a' 在 'b' 前面

八. 查找

std::map 提供了 find() 方法来检索具有特定键的元素:

  • find(const key_type& k)
    • 如果找到键 k,则返回一个指向该键的迭代器;如果未找到,则返回一个指向 map 结束 (end()) 的迭代器。
std::map<char, int> mymap;
mymap['a'] = 50;
mymap['b'] = 100;
mymap['c'] = 150;
mymap['d'] = 200;auto it = mymap.find('b');
if (it != mymap.end()) {mymap.erase(it); // 删除找到的元素
}

在这个例子中,我们查找键为 'b' 的元素,找到后使用返回的迭代器将其从 map 中删除。

通过这些解释,希望你能更清楚地理解 std::map 中的操作和方法。

九. 操作符

在 C++ 标准库中,std::map 提供了几个比较操作符,用于比较两个 map 对象。这些操作符包括 ==!=<<=>>=。这些操作符的行为和含义如下:

等于和不等于操作符 (== 和 !=)

  • ==(等于):当两个 map 的大小相等,并且每一个对应位置上的键值对都相等时,这两个 map 被认为是相等的。这意味着每个键值对的键和值都必须完全一致,并且它们的排列顺序也必须完全一样。由于 std::map 自动根据键进行排序,因此只要键值对相同,顺序自然会相同。
  • !=(不等于):如果两个 map 在大小或至少一个键值对上不相等,则这两个 map 被认为是不相等的。这是 == 的逻辑否定。

关系操作符 (<<=>>=)

  • <(小于):如果第一个 map 在字典顺序上比第二个 map 小,则返回 true。字典顺序是按照键来比较的,首先比较第一个键,如果相同则比较第二个键,依此类推。
  • <=(小于等于):如果第一个 map 小于或等于第二个 map(即第一个 map 是第二个的子集或完全相等),则返回 true
  • >(大于):如果第一个 map 在字典顺序上大于第二个 map,则返回 true
  • >=(大于等于):如果第一个 map 大于或等于第二个 map(即第一个 map 包含第二个 map 或完全相等),则返回 true

这些操作符使得 std::map 可以直接用在条件表达式中,非常适合进行集合比较、排序容器中的元素等操作。

#include <iostream>
#include <map>int main() {std::map<int, int> map1 {{1, 100}, {2, 200}};std::map<int, int> map2 {{1, 100}, {2, 200}};std::map<int, int> map3 {{1, 100}, {3, 300}};// 检查等于和不等于std::cout << "map1 == map2: " << (map1 == map2) << std::endl;std::cout << "map1 != map3: " << (map1 != map3) << std::endl;// 检查其他关系操作符std::cout << "map1 < map3: " << (map1 < map3) << std::endl;std::cout << "map3 > map1: " << (map3 > map1) << std::endl;return 0;
}

附录:

字典顺序(Lexicographical order)并不是指元素的大小(size),而是一种用于比较两个序列的方法,它按照元素的顺序逐个比较,直到找到一个不同的元素为止。这种比较方式类似于在字典中查找单词的顺序,因此得名“字典顺序”。在 C++ 中,这种比较方式通常用于字符串、数组、列表以及其他序列型容器。

字典顺序的具体规则:

当比较两个序列(例如两个 std::vectorstd::string 或 std::map 中的键序列)时,从两个序列的第一个元素开始比较:

  1. 比较元素:比较两个序列的当前元素。
    • 如果第一个序列的当前元素小于第二个序列的当前元素,则第一个序列在字典顺序中被认为是“小于”第二个序列。
    • 如果第一个序列的当前元素大于第二个序列的当前元素,则第一个序列在字典顺序中被认为是“大于”第二个序列。
  2. 相同则继续:如果当前元素相等,则比较下一个元素。
  3. 长度判别:如果所有对应元素都相等,那么序列的长度将用于决定顺序:
    • 较短的序列在字典顺序中被认为是“小于”较长的序列。

示例

考虑以下字符串的比较:

  • 字符串 "apple" 和 "banana":

    • 比较 'a' (apple) 和 'b' (banana)。由于 'a' < 'b',"apple" 在字典顺序中小于 "banana"。
  • 字符串 "book" 和 "bookcase":

    • "book" 和 "bookcase" 的前四个字符都相同,但 "book" 较短,因此在字典顺序中 "book" 小于 "bookcase"。

在 std::map 中的应用

当使用 <<=> 和 >= 操作符比较两个 std::map 对象时,比较是基于键的字典顺序进行的。std::map 的键会自动保持排序,因此比较两个 map 就是按键的字典顺序来比较的。如果一个 map 的键集合在字典顺序上小于另一个 map 的键集合,那么这个 map 就被认为在字典顺序上是“小于”另一个 map

这种字典顺序比较为 std::map 和其他容器提供了一种自然而直观的比较方式,使得它们可以直接用于排序和其他比较操作。

参考资料:

        http://blog.csdn.net/shuzfan/article/details/53115922

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

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

相关文章

深度强化学习(DRL)算法 附录 6 —— NLP 回顾之预训练模型篇

Self-Attention 模型结构 上图架构以 batch_size 为 1&#xff0c;两个时间步的 X 为例子&#xff0c;计算过程如下&#xff1a; 位置编码 根据 self-attention 的模型结构&#xff0c;改变 X 的输入顺序&#xff0c;不影响 attention 的结果&#xff0c;所以还需要引入额外的…

【教程】一个比较良心的C++代码混淆器

这是一个比较良心的C代码混淆器&#xff0c;用于信息竞赛训练和保护代码免受抄袭。本文将介绍这个混淆器的使用方法、混淆效果和已知的一些bug。同时&#xff0c;我们也会给出一些示例来演示混淆器的具体操作。 引言 在信息竞赛训练和实际开发中&#xff0c;保护代码的安全性和…

淘宝API商品详情数据在数据分析行业中具有不可忽视的重要性

淘宝商品详情数据在数据分析行业中具有不可忽视的重要性。这些数据为商家、市场分析师以及数据科学家提供了丰富的信息&#xff0c;有助于他们更深入地理解市场动态、消费者行为以及商品竞争态势。以下是淘宝商品详情数据在数据分析行业中的重要性体现&#xff1a; 请求示例&a…

Python实现exe小工具

1、实例代码 import tkinter as tk from tkinter import messagebox from tkinter import ttk import requestsdef submit():input_text entry.get()if len(input_text) 0:messagebox.showinfo("提示", "请输入您所要提问的问题&#xff01;")returnsel…

cesium加载倾斜影像数据(模拟雨、雪、雾、无人机飞行、测距、箭头标绘、电子围栏等)

实现效果如下&#xff1a; 功能菜单如下&#xff1a; 加载倾斜影像核心代码&#xff1a; var palaceTileset new Cesium.Cesium3DTileset({url: http://127.0.0.1:9002/tileset.json,//控制切片视角显示的数量&#xff0c;可调整性能maximumScreenSpaceError: 0.1,maximumNum…

C++ | Leetcode C++题解之第31题下一个排列

题目&#xff1a; 题解&#xff1a; class Solution { public:void nextPermutation(vector<int>& nums) {int i nums.size() - 2;while (i > 0 && nums[i] > nums[i 1]) {i--;}if (i > 0) {int j nums.size() - 1;while (j > 0 && …

HA-Maleimide-HA马来酰亚胺修饰透明质酸 水凝胶递送药物

HA-Maleimide-HA马来酰亚胺修饰透明质酸 水凝胶递送药物 【中文名称】马来酰亚胺修饰透明质酸 【英文名称】HA-Maleimide 【分 子 量】3k/5k/7k/10k/50k/100k/200k/300k/500k/1000k...... 【结 构 式】 【品 牌】碳水科技&#xff08;Tanshtech&#xff09; 【纯 度…

闲不住,手写一个数据库文档生成工具

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 逛博客的时候&#xff0c;发现了一个很有意思的文章&#xff1a;数据库表结构导…

【二分查找】Leetcode 74. 搜索二维矩阵【中等】

搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c…

面试:sleep 和 wait

一、共同点 wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权&#xff0c;进入阻塞状态 二、不同点 1、方法归属不同 sleep(long)是Thread的静态方法而wait(), wait(long)都是Object的成员方法&#xff0c;每个对象都有 2、醒来的时机不同 执行sleep(l…

Linux —— 进程控制

一、进程创建 —— fork 1.fork fork&#xff1a;在调用时&#xff0c;创建子进程&#xff0c;父进程返回子进程pid&#xff0c;子进程返回0&#xff0c;出错返回-1 头文件&#xff1a;#include<unistd.h> 2.fork函数被调用时&#xff0c;CPU做了什么&#xff1f; a…

使用SpringBoot3+Vue3开发公寓管理系统

项目介绍 公寓管理系统可以帮助公寓管理员更方便的进行管理房屋。功能包括系统管理、房间管理、租户管理、收租管理、房间家具管理、家具管理、维修管理、维修师傅管理、退房管理。 功能介绍 系统管理 用户管理 对系统管理员进行管理&#xff0c;新增管理员&#xff0c;修改…