【C++】STL map 与 multimap 用法和区别

参考:

  1. https://blog.csdn.net/shuzfan/article/details/53115922
  2. https://www.nhooo.com/cpp/cpp-map-swap-function.html
  3. https://blog.csdn.net/qq_34106574/article/details/86721916
  4. https://blog.csdn.net/zhongguoren666/article/details/8463249

一、std::map

1. 概述:

STL是标准C++系统的一组模板类,使用STL模板类最大的好处就是在各种C++编译器上都通用。

在STL模板类中,用于线性数据存储管理的类主要有vector、 list,、map 等等。

C++中map提供的是一种键值对容器,里面的数据都是成对出现的,每一对中的第一个值称之为关键字(key),每个关键字只能在map中出现一次;第二个称之为该关键字的对应值。

2. 用法:

(1)声明:
//头文件
#include <map>
//注意,STL头文件没有扩展名.h

map<int, string> ID_Name;

// 使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012
map<int, string> ID_Name = {
                { 2015, "Jim" },
                { 2016, "Tom" },
                { 2017, "Bob" } };
(2)插入操作:
  • 使用[ ]进行单个插入:
map<int, string> ID_Name;

// 如果已经存在键值2015,则会作赋值修改操作,如果没有则插入
ID_Name[2015] = "Tom";
  • 使用insert进行单个和多个插入:
    insert共有4个重载函数:
// 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
pair<iterator,bool> insert (const value_type& val);

//在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
iterator insert (const_iterator position, const value_type& val);

// 插入多个
void insert (InputIterator first, InputIterator last);

//c++11开始支持,使用列表插入多个   
void insert (initializer_list<value_type> il);

下面是具体使用示例:

#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 == false) {
        std::cout << "element 'z' already existed";
        std::cout << " with a value of " << ret.first->second << '\n';
    }

    //指定位置插入
    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));  //效率非最高

    //范围多值插入
    std::map<char, int> anothermap;
    anothermap.insert(mymap.begin(), mymap.find('c'));

    // 列表形式插入
    anothermap.insert({ { 'd', 100 }, {'e', 200} });

    return 0;
}
(3)取值:

Map中元素取值主要有at和[ ]两种操作,at会作下标检查,而[]不会。

map<int, string> ID_Name;

//ID_Name中没有关键字2016,使用[]取值会导致插入
//因此,下面语句不会报错,但打印结果为空
cout<<ID_Name[2016].c_str()<<endl;

//使用at会进行关键字检查,因此下面语句会报错
ID_Name.at(2016) = "Bob";
(4)容量查询:
// 查询map是否为空
bool empty();

// 查询map中键值对的数量
size_t size();

// 查询map所能包含的最大键值对数量,和系统和应用库有关。
// 此外,这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t max_size();

// 查询关键字为key的元素的个数,在map里结果非0即1
size_t count( const Key& key ) const; //
(5)迭代器:

共有八个获取迭代器的函数:begin, end, rbegin, rend 以及对应的 cbegin, cend, crbegin, crend

二者的区别在于:后者一定返回 const_iterator,而前者则根据map的类型返回iterator 或者 const_iterator。const情况下,不允许对值进行修改。如下面代码所示:

map<int,int>::iterator it;

map<int,int> mmap;
const map<int,int> const_mmap;

it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator

const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator

返回的迭代器可以进行加减操作,此外,如果map为空,则 begin = end。
在这里插入图片描述

(6)删除交换:
  • 删除:
// 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( iterator pos )

// 删除一定范围内的元素,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );

// 根据Key来进行删除,返回删除的元素数量,在map里结果非0即1
size_t erase( const key_type& key );

// 清空map,清空后的size为0
void clear();
//clear()就相当于enumMap.erase(enumMap.begin(), enumMap.end());

示例:

enumMap.erase(1);            //删掉关键字“1”对应的条目
enumMap.erase(enumMap.begin());        //删掉第一个条目
enumMap.erase(enumMap.begin(), enumMap.begin() + 1);    //删掉起始的两个条目
  • 交换:
    交换两个map的内容,但是两个map必须是相同类型的,尽管大小可能会有所不同
// 就是两个map的内容互换
void swap( map& other );

示例:
1.

#include <iostream>
#include <map>

using namespace std;

int main(void) {
   map<char, int> m1 = {
      {'a', 1},
      {'b', 2},
      {'c', 3},
      {'d', 4},
      {'e', 5},
      };

   map<char, int> m2;

   m2.swap(m1);

   cout << "Map包含以下元素" << endl;

   for (auto it = m2.begin(); it != m2.end(); ++it)
      cout << it->first << " = " << it->second << endl;

   return 0;
}

//输出:
Map包含以下元素
a = 1
b = 2
c = 3
d = 4
e = 5

在上面的示例中,Map m1具有五个元素,而m2为空。当您将m1交换为m2时,m1的所有元素都将交换为m2。
2.

#include <iostream>
#include <string>
#include <map>

using namespace std;

void show(const char *msg, map<string, int> mp);

int main() {
  map<string, int> m1, m2;

  m1.insert(pair<string, int>("A", 100));
  m1.insert(pair<string, int>("G", 300));
  m1.insert(pair<string, int>("B", 200));

  // 交换m1和m2的内容。
  cout << "交换m1和m2。\n";
  m1.swap(m2);
  show("Contents of m2: ", m2);
  show("Contents of m1: ", m1);

 // Clear m1.
  m1.clear();
  if(m1.empty()) cout << "m1 为空.";

  return 0;
}

// 使用迭代器显示map<string, int>的内容。
void show(const char *msg, map<string, int> mp) {
  map<string, int>::iterator itr;

  cout << msg << endl;
  for(itr=mp.begin(); itr != mp.end(); ++itr)
    cout << "  " << itr->first << ", " << itr->second << endl;
  cout << endl;
}

//输出:
交换m1和m2。
m2内容: 
  A, 100
  B, 200
  G, 300
m1内容: 
m1 为空.

在上面的示例中,Map m1的内容被交换到Map m2,并且在交换Map m1 后已被清除。

(7)顺序比较:
// 比较两个关键字在map中位置的先后
key_compare key_comp() const;

示例:

map<char,int> mymap;
map<char,int>::key_compare mycomp = mymap.key_comp();

mymap['a']=100;
mymap['b']=200;
mycomp('a', 'b');  // a排在b前面,因此返回结果为true
(8)查找:
  1. find(key):
// 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器
// 根据map的类型,返回的迭代器为 iterator 或者 const_iterator
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;

示例:

std::map<char,int> mymap;
std::map<char,int>::iterator it;

mymap['a']=50;
mymap['b']=100;
mymap['c']=150;
mymap['d']=200;

it = mymap.find('b');
if (it != mymap.end())
    mymap.erase (it); // b被成功删除
    
/*find的时候注意key的数据类型,最好用能消除数据类型差异的key,否则可能会出现强制转换后仍找不到的情况。
需要说明的是iterator, begin(), end()是STL模板类的一个通用概念,操作方法也大同小异;
通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator.first 和 iterator.second 分别代表关键字和存储的数据 */
  1. equal_range(key):
iterator map_name.equal_range(key)

equal_range是C++ STL中的一种二分查找的算法,试图在已排序的[first,last)中寻找value,它返回一对迭代器i和j,其中i是在不破坏次序的前提下,value可插入的第一个位置(亦即lower_bound),j则是在不破坏次序的前提下,value可插入的最后一个位置(亦即upper_bound),因此,[i,j)内的每个元素都等同于value,而且[i,j)是[first,last)之中符合此一性质的最大子区间。

equal_range根据键值,返回一对迭代器的pair对象。如果该键值在容器中存在,则pair对象中的第一个迭代器指向该键关联的第一个实例,第二个迭代器指向该键关联的最后一个实例的下一位置。如果找不到匹配的元素,则pair对象中的两个迭代器都将指向此键应该插入的位置。总之,equal_range返回迭代器位置区间 [ lower_bound, upper_bound )

算法lower_bound返回区间A的第一个迭代器,算法upper_bound返回区间A的最后一个元素的下一个位置,算法equal_range则是以pair的形式将两者都返回

示例:

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

int main ()
{
  map<char,int> mymap;
  pair<map<char,int>::iterator,map<char,int>::iterator> ret;

  mymap['a']=10;
  mymap['b']=20;
  mymap['c']=30;

  ret = mymap.equal_range('b');

  cout << "lower bound points to: ";
  cout << ret.first->first << " => " << ret.first->second << endl;

  cout << "upper bound points to: ";
  cout << ret.second->first << " => " << ret.second->second << endl;

  return 0;
}

//运行结果:
lower bound points to: 'b' => 20
upper bound points to: 'c' => 30
(9)操作符:

operator:== != < <= > >=
注意:对于==运算符, 只有键值对以及顺序完全相等才算成立。

(10)遍历:
#include<map>
#include<string>
#include<iostream>
using namespace std;
 
int main()
{
    map<string,int>  m;
    m["a"]=1;
    m["b"]=2;
    m["c"]=3;
    map<string,int>::iterator it;
    for(it=m.begin();it!=m.end();++it)
        cout<<"key: "<<it->first <<" value: "<<it->second<<endl;
    return   0;
}
map<string,int>::iterator it;   //定义一个迭代指针it。 
// it->first 为索引键值,it->second 为值。

在对象中应用时,最好在析构函数中要调用它的clear方法,例如:

class a{
    map<int,int> m;
 
   ~a(){
    m.clear();
  }
}

二、map 与 multimap 的区别:

map 与 multimap 都是存储key-value(键-值 对)类型的容器。

不同之处在于map只允许key与value一一对应;multimap一个key可对应多个value
在这里插入图片描述
示例:
以下不作特别说明,适用于map的都适用于multimap:

#include <opencv2/opencv.hpp>
#include <map>
#include <iostream>
#include <algorithm>    // std::sort

using namespace std;

//打印函数可写成模板

//打印函数 printfA
void printfA(map<vector<int>, int> vec_)
{
    for (std::map<std::vector<int>, int>::iterator it = vec_.begin(); it != vec_.end(); it++)
    {
        std::cout << it->first[0] << "  " << it->first[1] << "  " << it->first[2] << "  " << it->second << std::endl;
    }
    cout << "容积 = " << vec_.size() << endl;
}

//打印函数 printfB
void printfB(multimap<string, int> vec_)
{
    for (multimap<string, int>::iterator it = vec_.begin(); it != vec_.end(); it++)
    {
        std::cout << it->first  << "  " << it->second << std::endl;
    }
    cout << "容积 = " << vec_.size() << endl;
}

int main()
{
    //声明,定义
    std::map<int, std::string> m;
    m[3] = "h1";
    m[0] = "what";
    // 构建 key-value
    m.insert(std::pair<int, std::string>(2, "love you"));

    std::cout << m[0].c_str() << std::endl;//这里数字 不是索引值了
    std::cout << m[3].c_str() << std::endl;
    std::cout << m[4].c_str() << std::endl;
    std::cout << m[2].c_str() << std::endl;   // 会产生一个新的元素,即m[2] = ""

    m[6] = string("slam2345");
    std::cout << m.size() << std::endl;       // 5
  
    //遍历
    for (std::map<int, std::string>::iterator it = m.begin(); it != m.end(); it++)
        std::cout << it->first << ", " << it->second.c_str() << std::endl;

    std::vector<int> pointTemp;
    std::map<std::vector<int>, int> vecA;
    for (int y = 0; y < 4; y++)
    {
        for (int x = 0; x < 4; x++)
        {
            pointTemp.push_back(y);
            pointTemp.push_back(x);
            pointTemp.push_back(x + y);
            //插入
            vecA.insert(std::pair<std::vector<int>, int>(pointTemp, (y * 4 + x + 1)));
            pointTemp.clear();
        }
    }
    printfA(vecA);

注意区分map与multimap

  1. 两者都会自动排序;
  2. multimap插入不会覆盖已有键值对(对于map若有相同key,则拒绝插入)。
//***************************************************************************************************
//<1>插入返回值 判定是否插入成功
    //带插入数据
    vector<int> pointTemp_ = { 1,2,3 }; 
    // Insert方法不能覆盖,如果键已经存在,则插入失败;【注意插入位置,是自动排序】
    int a1 = 4;
    //判定插入是否成功
    pair< map<vector<int>, int>::iterator, bool> isInsertOK;//注意这里声明
    isInsertOK = vecA.insert(pair<vector<int>, int>(pointTemp_, a1));
    cout << "插入成功? " << isInsertOK.second << endl;
    //打印
    printfA(vecA);

//***************************************************************************************************
//<2>map对象的拷贝构造与赋值
    map<vector<int>, int> vecB(vecA); //拷贝构造
    map<vector<int>, int> vecC;
    vecC = vecA;
    vecC.swap(vecA);
    
//***************************************************************************************************
//<3>查找
     map<string, int> vecD;
    // 你以为按照下面初始化 vecD,他的size会是5? 由于insert方法不能覆盖,所以我们将map 改成 multimap
    vecD.insert(pair<string, int>((string)"china", 1));
    vecD.insert(pair<string, int>((string)"china", 2));//拒绝插入
    vecD.insert(pair<string, int>((string)"china", 3));//拒绝插入
    vecD.insert(pair<string, int>((string)"english", 1));
    vecD.insert(pair<string, int>((string)"english", 2));//拒绝插入

    multimap<string, int> vecE;
    vecE.insert(make_pair((string)"china", 1));
    vecE.insert(make_pair((string)"china", 1));//允许插入
    vecE.insert(make_pair((string)"china", 3));//允许插入
    vecE.insert(make_pair((string)"china", 4));//允许插入
    vecE.insert(make_pair((string)"china", 5));//允许插入
    vecE.insert(make_pair((string)"english", 1));
    vecE.insert(make_pair((string)"english", 2));//允许插入
    vecE.insert(make_pair((string)"america", 1));
    vecE.insert(make_pair((string)"america", 2));//允许插入
    vecE.insert(make_pair((string)"america", 3));//允许插入
    cout << "multimap 初始化" << endl;
    cout << "vecE所有元素"<<endl;
    printfB(vecE);
    
    //查找区间
    multimap<string, int> ::iterator it1 = vecE.lower_bound("china");   //指向vecE中第一个等于键值 “china”对应的元素
    multimap<string, int> ::iterator it2 = vecE.upper_bound("china");   //指向vecE中第一个大于键值 “china”对应的元素
    cout << it1->first << " " << it1->second << endl;
    cout << it2->first << " " << it2->second << endl;
    
    // 等于 = lower_bound + upper_bound
    pair<multimap<string, int>::iterator, multimap<string, int>::iterator > it3 = vecE.equal_range("china");
    map<string, int>::iterator it4 = it3.first;
    map<string, int>::iterator it5 = it3.second;

    //查找key = “china”键值对的个数
    int iCount = vecE.count("china");

    //查找key = “china”对应键值对
    multimap<string, int>::iterator it6 = vecE.find("china");

//***************************************************************************************************
// <4>删除
    multimap<string, int>::iterator itBegin = vecE.begin();
    // 删除 vecE 前面三个元素 与 后面 三个元素
    // 在改善特征点匹配算法中有实践
    int index = 0;
    int vecA_size = vecE.size();
    //删除(自定义删除任何元素)
    for (multimap<string, int>::iterator it_ = vecE.begin(); it_ != vecE.end(); )
    {
        //<1>法1
        //vecE.erase(it_++);
        //<2>法2
        if ((0<=index)&&(index<=2))
        {
            it_ = vecE.erase(it_);//这样写,防止指针失效
        }

        else if (((vecA_size - 3) <= index) && (index <= (vecA_size - 1)))
        {
            it_ = vecE.erase(it_);
        }
        else
        {
            it_++;
        }
        ++index;
    }

    //删除multimap中key = "english"的 所有 元素
    vecE.erase("english");
    cout << "vecE删除key = english的 所有 元素 " << endl;
    printfB(vecE);

    //删除所有元素
    multimap<string, int>::iterator itBegin_ = vecE.begin();
    multimap<string, int>::iterator itEnd_ = vecE.end();
    vecE.erase(itBegin_, itEnd_);
    //bool isEmpty = vecE.empty();

    if (vecE.empty())
    {
        cout << "vecE已经被清空" << endl;
    }
    return 0;
}
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ STL中的mapmultimap是关联容器,用于存储键值对(key-value pairs),其中每个键(key)唯一对应一个值(value)。 map是一个有序容器,根据键的大小进行自动排序,默认按照键的升序进行排序。每个键只能在map中出现一次,如果尝试插入具有相同键的元素,新元素将替代旧元素。 multimap也是一个有序容器,与map不同的是,它允许多个具有相同键的元素存在。多个具有相同键的元素将按照插入的顺序进行存储,而不会自动排序。 这两个容器都提供了一系列的操作函数,如insert、erase、find等,用于插入、删除和查找元素。 以下是一个使用map的简单示例: ```cpp #include <iostream> #include <map> int main() { std::map<std::string, int> scores; scores.insert(std::make_pair("Alice", 90)); scores.insert(std::make_pair("Bob", 80)); scores.insert(std::make_pair("Charlie", 70)); // 查找并输出Bob的分数 std::cout << "Bob's score: " << scores["Bob"] << std::endl; // 遍历并输出所有键值对 for (const auto& pair : scores) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; } ``` 上述示例中,我们创建了一个存储string类型键和int类型值的map容器scores。通过insert函数依次插入了三个键值对。然后我们通过scores["Bob"]来获取Bob的分数,并输出结果为80。 接着我们使用范围-based for循环遍历map中的所有键值对,并输出每个键值对的键和值。 multimap用法map类似,只是它允许多个具有相同键的元素存在。 这些关联容器在查找和插入操作上具有较高的效率,特别适用于需要根据键进行快速查找的场景。在实际应用中,你可以根据自己的需求选择适合的容器类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值