首页 > 编程语言 >C++容器map的常见用法

C++容器map的常见用法

时间:2024-07-05 22:26:37浏览次数:12  
标签:std map const 容器 元素 C++ 用法 排序

文章目录

1.map和multimap容器

在C++ STL(标准模板库)中,mapmultimap是两种关联容器,它们用于存储键值对,其中每个键都是唯一的,并且与一个特定的值相关联。然而,它们之间存在一些关键区别:

map

  1. 唯一性map中的键是唯一的,也就是说,一个键只能对应一个值。如果你尝试插入一个键值对,而该键已经存在于map中,那么原有的键对应的值将被新值替换。

  2. 排序map中的元素按照键的升序(默认情况下)进行排序。这是通过比较键实现的,可以自定义比较函数来改变排序方式。

  3. 基本操作:主要操作包括插入(insert)、删除(erase)、查找(find)和访问(直接通过键访问,如map[key])元素。

multimap

  1. 非唯一性:与map的主要区别在于,multimap允许有多个相同的键,每个键可以对应多个不同的值。这意味着当你插入一个键已经存在的键值对时,新值会被添加到该键已有的值集合中,而不是替换原有值。

  2. 排序:和map一样,multimap中的元素也是根据键进行排序的,可以是升序或根据自定义的比较函数排序。

  3. 基本操作:除了与map共享的基本操作外,由于可能有多个相同键的存在,multimap的查找操作(如find)可能会返回一个迭代器范围,表示所有匹配键的值集合。

使用场景

  • map适用于当你需要确保每个键只对应一个值,且经常需要根据键快速查找或更新其关联值的场景。

  • multimap适合那些一个键可能对应多个值,且在查询时你可能关心所有与某一键相关联的值的情况,比如存储学生的分数时,如果有多个学生获得相同的分数,multimap能很好地处理这种情况。

1.map容器的构造和赋值

在C++中,std::map容器提供了多种构造函数和赋值操作,以方便初始化和管理键值对数据。

1.构造函数
  1. 默认构造函数

    std::map<Key, T> m; // 创建一个空的map,默认按键升序排序。
    
  2. 拷贝构造函数

    std::map<Key, T> m1;
    // 假设m1已经被初始化
    std::map<Key, T> m2(m1); // m2是m1的一个拷贝
    
  3. 使用比较函数构造

    struct MyCompare {
        bool operator()(const Key& lhs, const Key& rhs) const {
            // 自定义比较逻辑
            return lhs < rhs; // 示例:使用默认比较
        }
    };
    std::map<Key, T, MyCompare> m(MyCompare()); // 使用自定义比较函数
    
  4. 区间构造

    std::map<Key, T> m.begin(), m.end());
    // 或者使用其他容器/迭代器范围
    std::vector<std::pair<Key, T>> vec;
    // 假设vec已被填充
    std::map<Key, T> m(vec.begin(), vec.end());
    
2.赋值操作
  1. 拷贝赋值运算符

    std::map<Key, T> m1, m2;
    // 假设m1和m2已经被初始化
    m1 = m2; // 将m2的内容赋值给m1
    
  2. 初始化列表赋值(C++11及以上)

    std::map<Key, T> m = { {key1, value1}, {key2, value2}, ... };
    
  3. 区间赋值

    std::map<Key, T> m1, m2;
    // 假设m2已被填充
    m1.assign(m2.begin(), m2.end());
    
  4. 插入迭代器对
    虽然这不是直接的赋值操作,但可以通过迭代器对插入元素来“赋值”或填充map。

    std::map<Key, T> m;
    std::vector<std::pair<Key, T>> vec;
    // 假设vec已被填充
    m.insert(vec.begin(), vec.end());
    

这些构造和赋值方法提供了灵活的方式来创建和管理std::map容器,满足不同场景下的需求。

 ```cpp
 #include <iostream>
 #include <map>
 
 // 使用标准命名空间,简化后续代码中的类型名称
 using namespace std;
 
 /**
  * 打印map的内容
  * @param m 一个整数到整数的映射,其内容将被打印
  */
 void printMap(const map<int,int>& m){
     // 遍历映射中的所有元素,并打印每个元素的键和值
     for(auto it : m){
         cout << it.first << " " << it.second << endl;
     }
 }
 
 int main(){
     // 初始化一个map,包含两个元素
     map<int,int> m = {{1,2},{2,4}};
     // 创建一个对m的常量引用,以避免复制map的内容
     const map<int,int>& m1(m);
     // 调用函数打印map的内容
     printMap(m);
     printMap(m1);
     // 初始化一个空的map,并将其设置为与m相同的内容
     map<int,int> m2;
     m2 = m;
     printMap(m2);
 
     return 0;
 }
 ```
2.map容器的大小和交换

在C++的STL中,std::map容器提供了以下与大小和交换相关的成员函数:

1.获取大小
  • size(): 此函数返回map中元素的数量,即键值对的总数。

    std::map<Key, T> myMap;
    // ... 添加元素到myMap
    std::cout << "Size of the map: " << myMap.size() << std::endl;
    
  • empty(): 判断map是否为空,如果容器中没有元素,则返回true,否则返回false

    if (myMap.empty()) {
        std::cout << "The map is empty." << std::endl;
    } else {
        std::cout << "The map is not empty." << std::endl;
    }
    
2.交换内容
  • swap(std::map<Key, T>& other): 此函数交换两个map容器的内容,交换操作高效执行,不会涉及元素的复制或移动。

    std::map<Key, T> myMap1, myMap2;
    // ... 分别填充myMap1和myMap2
    myMap1.swap(myMap2);
    // 现在myMap1包含myMap2原来的内容,反之亦然
    

这些函数允许检查容器的当前状态(是否为空,包含多少元素)以及重新分配两个容器的内容,而无需创建新的容器副本。这对于优化内存使用和性能特别有用。

//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>

// 使用标准命名空间,简化后续代码中标准库类型的引用
using namespace std;

/**
 * 打印map的内容
 * @param m 一个整数到整数的映射,其内容将被打印
 */
void printMap(const map<int,int>& m){
    // 遍历map,打印每个键值对
    for(auto it : m){
        cout << it.first << " " << it.second << endl;
    }
}

int main(){
    // 初始化一个map,包含两个元素
    map<int,int> m{{1,2},{2,4}};
    // 调用函数打印map内容
    printMap(m);
    // 打印map的大小
    cout << m.size() << endl;
    // 检查map是否为空,如果不为空,则再次打印其内容
    if(!m.empty()){
        printMap(m);
    }
    // 初始化另一个空的map
    map<int,int> m1;
    // 交换两个map的内容
    m.swap(m1);
    // 分别打印交换后的两个map的内容
    printMap(m);
    printMap(m1);
    return 0;
}
3.map容器的插入和删除

在C++的STL中,std::map容器提供了丰富的成员函数来进行元素的插入和删除操作。以下是几种常见的插入和删除方法:

1.插入元素
  1. insert(const value_type& val): 插入一个键值对。如果键已存在,则不会插入重复的键。

    myMap.insert(std::make_pair(key, value));
    // 或者使用直接初始化语法(C++11起)
    myMap.insert({key, value});
    
  2. insert(iterator hint, const value_type& val): 尝试在迭代器hint指向的位置附近插入元素,以提高效率。如果hint恰好指向要插入位置的下一个位置,则插入操作可能更快。

    auto it = myMap.lower_bound(key);
    myMap.insert(it, std::make_pair(key, value));
    
  3. insert(initializer_list<value_type> ilist): 从初始化列表插入多个键值对(C++11起)。

    myMap.insert({ {key1, value1}, {key2, value2}, ... });
    
2.删除元素
  1. erase(iterator position): 删除迭代器position指向的元素。

    auto it = myMap.find(key);
    if (it != myMap.end()) {
        myMap.erase(it);
    }
    
  2. erase(const key_type& key): 删除具有指定键的元素。

    myMap.erase(key);
    
  3. erase(iterator first, iterator last): 删除迭代器范围[first, last)内的所有元素。

    auto startIt = myMap.lower_bound(startKey);
    auto endIt = myMap.upper_bound(endKey);
    myMap.erase(startIt, endIt);
    
3.注意事项
  • 在删除元素后,之前指向被删除元素的迭代器、指针和引用都会变得无效。
  • erase返回的是下一个元素的迭代器,这在连续删除元素时很有用。
  • 对于insert操作,如果试图插入的键已经存在,该操作不会改变容器且不会抛出异常(除非因内存分配失败)。

通过这些方法,你可以有效地管理std::map容器中的数据,无论是添加新元素还是移除不需要的条目。

//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>

// 使用标准命名空间,简化后续代码中标准库类型的引用
using namespace std;

/**
 * 打印map的内容
 * @param m 一个整数到整数的映射,其内容将被打印
 */
void printMap(const map<int,int>& m){
    // 遍历map,打印每个键值对
    for(auto it : m){
        cout << it.first << " " << it.second << endl;
    }
}

int main(){
    // 初始化一个map,包含两个元素
    map<int,int> m{{1,2},{2,4}};
    // 调用函数打印map内容
    printMap(m);
    // 打印map的大小
    cout << m.size() << endl;
    // 检查map是否为空,如果不为空,则再次打印其内容
    if(!m.empty()){
        printMap(m);
    }
    // 初始化另一个空的map
    map<int,int> m1;
    // 交换两个map的内容
    m.swap(m1);
    // 分别打印交换后的两个map的内容
    printMap(m);
    printMap(m1);
    return 0;
}
4.map容器的查找和统计

在C++的STL中,std::map容器提供了几种查找和统计元素的方法:

1.查找元素
  1. find(const key_type& k): 返回一个迭代器,指向键为k的第一个元素。如果找不到这样的元素,则返回end()

    auto it = myMap.find(key);
    if (it != myMap.end()) {
        std::cout << "Found element with key: " << it->first << ", value: " << it->second << std::endl;
    } else {
        std::cout << "Element not found." << std::endl;
    }
    
  2. count(const key_type& k): 返回键为k的元素数量。对于map,由于键唯一,此函数要么返回0(表示不存在),要么返回1(表示存在)。

    size_t count = myMap.count(key);
    if (count > 0) {
        std::cout << "Key exists in the map." << std::endl;
    } else {
        std::cout << "Key does not exist in the map." << std::endl;
    }
    
2.统计元素

map容器中,由于键的唯一性,统计通常较为直接,主要依赖于count函数来确定某个键是否存在。对于键值对的计数,通常意义不大,因为每个键最多只能对应一个值。但在多值映射multimap中,count可以有效统计相同键值出现的次数。

3.示例

假设有一个std::map<int, std::string>类型的myMap,以下是如何使用查找和统计方法的简单示例:

#include <iostream>
#include <map>

// 使用标准命名空间,简化后续代码中标准库的调用
using namespace std;

/**
 * 打印map中的所有元素
 * @param m 一个整数到整数的映射,其元素将被打印
 */
void printMap(const map<int,int>& m){
    // 遍历map中的每个元素,并打印其键值对
    for(auto it : m){
        cout << it.first << " " << it.second << endl;
    }
}

int main(){
    // 初始化一个map,包含两个元素:{1, 2} 和 {2, 4}
    map<int,int> m{{1,2},{2,4}};
    
    // 寻找键值为1的元素
    auto it = m.find(1);

    cout << it->first << endl;
    // 检查是否找到键值为1的元素
    if(it != m.end()){
        cout << "in" << endl;
    } else{
        cout << "no" << endl;
    }
    
    // 调用函数打印map的所有元素
    printMap(m);
    
    // 打印键值为1的元素在map中出现的次数
    cout << m.count(1) << endl;
    
    return 0;
}
5.map容器的排序

std::map容器在C++ STL中自动保持其元素有序。默认情况下,元素是按照键(key)的升序进行排序的。这种排序是由比较函数决定的,std::map默认使用std::less<Key>作为比较器,它提供了一个严格弱序的比较,适用于大多数基本数据类型。

1.自定义排序规则

尽管std::map默认按key排序,但如果你想改变排序逻辑,可以在创建map时指定一个自定义的比较函数对象。例如,如果你想让map按照key降序排列,或者基于键的某些复杂属性排序,你可以这样做:

struct CustomCompare {
    bool operator()(const Key& lhs, const Key& rhs) const {
        // 自定义比较逻辑,例如降序排序:
        return rhs < lhs; // 改变比较逻辑以实现降序
    }
};

std::map<Key, Value, CustomCompare> myMap;
2.注意点
  • 不可更改排序:一旦map实例化,其排序规则就不能再改变。如果需要改变排序,你需要创建一个新的map实例并使用不同的比较函数。
  • 基于value排序:如果需要按照value排序,std::map本身不直接支持。一个常见做法是将map的元素复制到一个std::vector中,每个元素是一个键值对(例如,使用std::pair<Key, Value>),然后对这个vector进行排序。可以使用std::sort函数配合自定义的比较器来达到目的。
3.示例:基于value排序
std::map<Key, Value> originalMap;
// ... 填充originalMap

std::vector<std::pair<Key, Value>> items(originalMap.begin(), originalMap.end());
std::sort(items.begin(), items.end(), [](const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) {
    return a.second > b.second; // 降序排序基于value
});

// 现在items是按value排序的键值对集合

综上所述,虽然std::map自动维持其内部元素的排序(基于键),但通过自定义比较函数,可以控制排序逻辑。而对于基于value的排序,则需借助额外的数据结构和排序算法。

//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>
using namespace std;

// 自定义比较函数对象,用于map中键值的降序排序
class Compare{
public:
    // 比较两个整数,返回true如果第一个大于第二个
    bool operator()(const int &a,const int &b) const{
        return a > b;
    }
};

// 打印map中的所有元素
// 参数m: 使用自定义比较函数对象Compare的map
void printMap(const map<int, int, Compare>& m){
    // 遍历map中的每个元素并打印
    for(auto it : m){
        cout << it.first << " " << it.second <<endl;
    }
}

int main(){
    // 初始化一个使用自定义比较函数对象的map
    map<int,int,Compare>m{{1,2},{2,4},{3,5}};
    // 调用函数打印map内容
    printMap(m);
    return 0;
}

标签:std,map,const,容器,元素,C++,用法,排序
From: https://blog.csdn.net/weixin_73497355/article/details/140218866

相关文章

  • C++:win11下的VScode构建百度Comate
    一. VScode的安装官网下载地址:https://code.visualstudio.com/ (选择好对应的系统版本)汉化:安装完软件后,打开软件界面的"扩展商店",搜索Chinese,下载相应的插件,如下图:二. 配置C++开发环境VSCode只是一个高级的编辑器,可以用来写C++代码,不能直接编译代码。所以,如果......
  • 【并查集】浅谈思想 & 代码实现 & 实战例题(C/C++)
    思想综述并查集(Union-Find)算法的主要操作包括两种:合并(Union):将两个不相交的集合合并成一个集合。查询(Find):查询两个元素是否属于同一个集合。并查集算法的核心思想是使用树(通常是森林)来表示这些不相交的集合,其中每个集合被表示为一棵树,树的根节点代表这个集合的标识(或称为代表......
  • autoware.universe源码略读(3.5)--perception:compare_map_segmentation/crosswalk_tra
    autoware.universe源码略读3.5--perception:compare_map_segmentation/crosswalk_traffic_light_estimatorcompare_map_segmentationcompare_elevation_map_filter_nodedistance_based_compare_map_filter_nodeletvoxel_based_approximate_compare_map_filter_nodeletvox......
  • BMP(Bitmap Image File)解码流程:
    BMP(BitmapImageFile)的解码流程主要包括以下几个步骤,这些步骤确保了BMP图像文件能够被正确地读取并显示在屏幕上:1.读取文件头信息目的:确认文件类型和基本信息。内容:BMP文件以特定的文件头标识开始,这包括文件的类型(必须是“BM”,以十六进制表示为0x4D42)、文件大小、保留字(......
  • 引用个数为什么会影响内存泄漏 c++例子
    在C++中,内存泄漏通常与手动管理内存有关,而不是直接由引用计数引起,因为C++标准库本身并不提供自动的引用计数功能。但是,我们可以通过一个例子来间接说明引用(或指针)管理不当如何导致内存泄漏,尤其是当涉及复杂对象结构和所有权关系时,这种管理不当往往体现在循环引用上。基本概念......
  • C++实现计算程序运行的时间
    在C++中,计算程序运行时间可以使用多种方法,其中<chrono>库提供了精确的时间测量功能,适用于C++11及更高版本。以下是一个简单的示例,展示如何使用std::chrono来计算程序运行时间:#include<iostream>#include<chrono>intmain(){  //获取程序开始执行的时间点  au......
  • C++ 基础的输入输出介绍
    C++基础的输入输出介绍在C++编程的世界中,输入输出是连接程序与用户界面的桥梁,是实现人机交互不可或缺的部分。对于初学者而言,掌握C++中基本的输入输出方式——使用cin进行输入和使用cout进行输出,是踏入C++编程大门的第一步。本文将详细介绍如何在C++程序中利用cin和cout来......
  • C++ UTF-8编解码
    icu编解码数据:externconstUConverterSharedData  _MBCSData,_Latin1Data,  _UTF8Data,_UTF16BEData,_UTF16LEData,_UTF32BEData,_UTF32LEData,  _ISO2022Data,   _LMBCSData1,_LMBCSData2,_LMBCSData3,_LMBCSData4,_LMBCSData5,_LMBCSData6......
  • C++——模板详解(下篇)
    一、非类型模板参数模板参数分为类型形参与非类型形参。类型形参即:出现在模板参数列表中,跟在class或者typename之后的参数类型名称。非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。namespaceH{//定义一个模板类型的......
  • C++list的模拟实现
    链表节点 template<classT> structListNode { ListNode(constT&data=T()) : _data(data) { } ListNode<T>*_prev=nullptr; ListNode<T>*_next=nullptr; T_data; };因为之后要访问这个类的成员变量函数和结构体,所以在这里将class直接改为struct......