8

C++中STL标准容器类映射MAP简单入门使用详解,MAP容器类简单相关实例

 3 years ago
source link: https://blog.popkx.com/introduce-of-MAP-in-STL-of-C-and-simple-examples/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

之前几篇文章讨论的C++语言中的 STL 模板容器类存放的都是单一类型的数据:

本文将介绍C++语言中的 STL 模板容器类:map(映射),它是一个关联容器类,内部存放的是 key-value 键值对,其中的“key(键)”总是独一无二的,可以从容器中删除它,也可以把新的key-value插入到容器中,但是一旦插入,key 本身就无法修改了,其对应的 value 倒是可以修改,下面将通过实例看到这一点。

声明和初始化

C++语言程序开发中,要使用 map 容器,需要包含<map>头文件:

#include <map>

map 容器中的 key-value 的数据类型可以是任意的,定义一个 map 对象的C++语言代码可以如下写:

std::map<key_type, value_type> val;

map在标准命名空间std中,下文为了方便,实例代码不再写std::

其中的 key_typevalue_type 分别表示 key-value 中的 key 的数据类型和 value 的数据类型,例如:

map <int, int> mymap;

这表示 map 对象 mymap 容器中存放的将是 int-int 类型的数据,在C++11标准的支持下,可以在定义 map 对象的同时赋初值,例如:

map<int, int> mymap {{1, 10}, {2, 20}, {3, 30}};

上面这行C++语言代码执行后,mymap 容器中将有 3 个键值对。应该注意,在往 map 容器中插入元素时,必须以 key-value 的形式,而不能只插入 key 或者只插入 value。

操作 map

C++语言中的 map 容器类的基本操作同前面介绍的几个容器类大体相同,常用的包括以下几个操作(或者说成员函数):

  • at 和 []:用于访问 map 容器中的元素,at 和 [] 的功能基本相同,只有一个区别:如果访问 map 容器中不存在的元素,at 将抛出一个异常,而 [] 操作符则将这个“不存在的元素”插入到 map 容器中。
  • begin:返回 map 容器中第一个元素的迭代器指针。
  • end:返回 map 容器末尾位置(最后一个元素的下一个位置)的迭代器指针。

下面是一段C++语言代码示例,该示例主要使用了前文提到的点,请看:

#include <iostream>
#include <map>

using namespace std;

int main()
{
    map<int, int> mymap {{1,10},{2,20},{3,30}};
    // 遍历 mymap
    map<int,int> :: iterator it;
    cout << "\nThe map mymap is : \n";
    cout << "\tKEY\tVALUE\n";
    for (it = mymap.begin(); it != mymap.end(); ++it) {
        cout << '\t' << it->first
        << '\t' << it->second << '\n';
    }
    cout << endl;

    mymap.at(2) = 10;    // 修改 key(2) 对应的值
    mymap[3] = 10;        // 修改 key(3) 对应的值
    //遍历修改后的 mymap
    cout << "\nThe map mymap is : \n";
    cout << "\tKEY\tVALUE\n";
    for (it = mymap.begin(); it != mymap.end(); ++it) {
        cout << '\t' << it->first
        << '\t' << it->second << '\n';
    }
    cout << endl;

    return 0;
}

上面这段C++语言代码很简单:先定义了一个 map 对象 mymap,并且初始化了 3 个键值对,然后将之打印出来。在打印过程中可以看出,迭代器指针的 first 成员对应着 key,而 second 成员则对应着 value。之后,通过 at 和 [] 操作符分别将 key(2) 和 key(3) 对应的值修改为 10,再打印出修改后的 mymap。编译这段C++语言代码时注意指定-std=c++11,输出如下:

# g++ t1.cpp -std=c++11
# ./a.out 

The map mymap is : 
    KEY    VALUE
    1    10
    2    20
    3    30


The map mymap is : 
    KEY    VALUE
    1    10
    2    10

一切与预期一致。当然了,map 容器类比较常用的操作方法还有一些:

  • insert(key, value):将键值对(key, value)插入到 map 容器中。
  • erase:从 map 容器中删除元素,该方法有两个版本:

    • erase(pos) - 删除 map 容器中指定位置 pos 处的元素。
    • erase(key) - 删除 key 对应的元素。
  • empty:检查 map 容器是否没有元素。
  • size:返回 map 容器中元素的数目。
  • max_size:返回 map 容器能够容纳的最多元素数目。
  • clear:删除 map 容器中的所有元素。
  • count:返回容器中给定 key 对应的元素数目。

下面是一段C++语言代码示例,请看:

#include <iostream>
#include <map>
using namespace std;
int main()
{
   map<int,int> mymap;
   mymap.insert(pair<int, int>(1, 1));
   mymap.insert(pair<int, int>(2, 3));
   mymap.insert(pair<int, int>(3, 5));
   mymap.insert(pair<int, int>(4, 7));
   mymap.insert(pair<int, int>(5, 9));
   mymap.insert(pair<int, int>(6, 11));
   mymap.insert(pair<int, int>(7, 13));
   
   map<int,int> :: iterator it;
   cout << "\nThe map mymap is : \n";
   cout << "\tKEY\tVALUE\n";
   for (it = mymap.begin(); it != mymap.end(); ++it) {
      cout << '\t' << it->first
      << '\t' << it->second << '\n';
      }
   cout << endl;
   cout<<"\n mymap.size(): "<<mymap.size();
   cout<<"\n mymap.max_size(): "<<mymap.max_size();
   cout<<endl;
   int num;
   num = mymap.erase(3);
   cout << "\nmymap.erase(3) : ";
   cout << num << " removed \n";
   cout << "\tKEY\tELEMENT\n";
   for (it = mymap.begin(); it != mymap.end(); ++it) {
      cout << '\t' << it->first
      << '\t' << it->second << '\n';
      }
   cout << endl;
   mymap.clear();
   cout<<"\nAfter clear operation mymap.size() : "<<mymap.size() <<endl;

   return 0;
}

上面的C++语言代码首先使用 insert() 函数插入了 7 组键值对,并且打印了它们,接着通过 size() 和 max_size() 函数分别输出了当前容器 mymap 中的元素数目,和能够容纳的最多数目(这主要取决于机器内存)。

接着,代码调用了 erase(3) 函数删除了 key(3) 对应的元素,并打印了出来。最后代码调用了 clear() 函数删除了容器中所有的元素,此时 size() 函数将返回 0。

同样的,编译这段C++语言代码需要指定-std=c++11选项,最终输出如下,请看:

# g++ t2.cpp -std=c++11
# ./a.out 

The map mymap is : 
    KEY    VALUE
    1    1
    2    3
    3    5
    4    7
    5    9
    6    11
    7    13


 mymap.size(): 7
 mymap.max_size(): 461168601842738790

mymap.erase(3) : 1 removed 
    KEY    ELEMENT
    1    1
    2    3
    4    7
    5    9
    6    11
    7    13
After clear operation mymap.size() : 0

多重映射 multimap

multimap 和 map 在绝大多数方面都是相似的,比较明显的区别是 multimap 允许多个元素拥有相同的 key,所以,multimap 中的元素的 key 可以不是独一无二的,不过这样一来,就不能再通过 at 和 [] 方法访问元素了。

操作 multimap 容器类的方法与操作 map 容器类的方法大体相同,下面是一段C++代码示例:

#include <iostream>
#include <map>

using namespace std;

int main()
{
   multimap<int,int> mymap;
   mymap.insert(pair<int, int>(1, 1));
   mymap.insert(pair<int, int>(2, 3));
   mymap.insert(pair<int, int>(3, 5));
   mymap.insert(pair<int, int>(3, 7));
   mymap.insert(pair<int, int>(4, 9));
   
   map<int,int> :: iterator it;
   cout << "\nThe multimap mymap is : \n";
   cout << "\tKEY\tVALUE\n";
   for (it = mymap.begin(); it != mymap.end(); ++it) {
      cout << '\t' << it->first
      << '\t' << it->second << '\n';
      }
   cout << endl;
   cout<<"\n mymap.size(): "<<mymap.size();
   cout<<"\n mymap.max_size(): "<<mymap.max_size();
   cout<<endl;
   int num;
   num = mymap.erase(3);
   cout << "\nmymap.erase(3) : ";
   cout << num << " removed \n";
   cout << "\tKEY\tELEMENT\n";
   for (it = mymap.begin(); it != mymap.end(); ++it) {
      cout << '\t' << it->first
      << '\t' << it->second << '\n';
     }
   cout << endl;
   mymap.clear();
   cout<<"\nAfter clear operation mymap.size() : "<<mymap.size();

   return 0;
}

这段C++语言代码与前面操作 map 容器类的代码几乎相同,编译这段代码并执行,得到如下输出:

# g++ t3.cpp -std=c++11
# ./a.out 

The multimap mymap is : 
    KEY    VALUE
    1    1
    2    3
    3    5
    3    7
    4    9


 mymap.size(): 5
 mymap.max_size(): 461168601842738790

mymap.erase(3) : 2 removed 
    KEY    ELEMENT
    1    1
    2    3
    4    9


After clear operation mymap.size() : 0

我们将注意力放在 key(3) 上,从输出来看,multimap 容器类的确允许同一个 key 被多个元素使用,但是也要注意,因为不能保证唯一性,multimap 容器类没有 at 和 [] 方法。

无序映射 unordered_map

和普通的 map 一样,C++语言中的 unordered_map 类也是一种存储 key-value 对的容器。只不过,unordered_map 中的元素可以是无序的。

与普通的 map 一样,unordered_map 容器中的 key 也是用于映射 value 的,相关的 key-value 类型也可以是任意的。

unordered_map 容器类内部的无序映射的实现是由“哈希表”数据结构提供的,key 对应着哈希表中的索引,因此,unordered_map 容器类的性能在很大程度上取决于所提供的哈希函数。

使用 unordered_map 容器类需要包含头文件 <unordered_map>,实例化一个对象和普通 map 没有什么区别:

#include <unordered_map>
std::unordered_map<key_type, value_type> val;

因为 unordered_map 容器内部使用的是哈希表数据结构,所以相比于普通 map 容器类多出了几个方法:

  • bucket:返回容器中 key 对应元素的 bucket 数目。
  • bucket_count:返回容器中所有的 bucket 数目。
  • bucket_size:返回每个 bucket 中的元素数目。

关于 bucket,之后的文章会讨论。

下面是一段C++语言代码示例,请看:

#include <iostream> 
#include <unordered_map> 

using namespace std; 

int main() 
{     
    unordered_map<string, int> umap; 
  
    // 使用 [] 操作符插入元素
    umap["RED"] = 1; 
    umap["GREEN"] = 2; 
    umap["BLUE"] = 3; 
    
    // 使用 insert() 函数插入元素
    umap.insert(make_pair("YELLOW", 4)); 
  
    unordered_map<string, int>:: iterator it; 
    cout << "\nAll Elements in unordered map umap : \n"; 
    for (it = umap.begin(); it != umap.end(); ++it) 
    { 
        
        cout << it->first << "\t" << it->second << endl; 
    } 
    cout<<endl;
    cout<<"bucket of RED: "<<umap.bucket("RED") <<endl;
    cout<<"umap bucket_count : "<<umap.bucket_count()<<endl;
    cout<<"size of first bucket: "<<umap.bucket_size(1)<<endl;

    return 0;
}

上述C++语言代码的前半段和操作普通的 map 容器类没什么区别,后半段主要打印了几个 bucket 相关的值。编译这段代码并执行,得到如下输出:

# g++ t4.cpp -std=c++11
# ./a.out 

All Elements in unordered map umap : 
YELLOW    4
BLUE    3
GREEN    2
RED    1

bucket of RED: 1
umap bucket_count : 11
size of first bucket: 1

本文主要讨论了C++语言中的标准容器类map及其相关的两个变种类multimapunordered_map的基本使用,其内部实现倒是浅尝辄止了。不过,学会使用它们对理解它们是有所帮助的,熟练后再去理解相关的详细设计和实现,绝对是事半功倍的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK