map/unordered_map基础用法_unordered_map用法-程序员宅基地

技术标签: C++/C  机器学习  计算机视觉  深度学习  

参考  map/unordered_map基础用法 - 云+社区 - 腾讯云

map是STL里重要容器之一。它的特性总结来讲就是:所有元素都会根据元素的键值key自动排序(也可根据自定义的仿函数进行自定义排序),其中的每个元素都是<key, value>的键值对,map中不允许有键值相同的元素,因此map中元素的键值key不能修改,但是可以通过key修改与其对应的value。如果一定要修改与value对应的键值key,可将已存在的key删除掉,然后重新插入。

定义原型:

它作用应用场景可用作 ①字典    ②统计次数


(1)插入操作

方式有3种

 注:typedef pair<const Key, T> value_type;

通过插入新元素来扩展容器,通过插入元素的数量有效地增加容器容量。

由于映射中的元素键是唯一的,因此插入操作将检查每个插入的元素是否具有与容器中已有元素相同的键,如果是,则不插入该元素,并将迭代器返回给此现有元素如果函数返回一个值)。

对于允许重复元素的类似容器,请参阅multimap。

在map中插入元素的另一种方法是使用成员函数map :: operator []。

在容器内部,map容器按照其比较对象指定的标准,通过键将所有元素进行排序。这些元素总是按照这个顺序插入到相应的位置。

返回值:

1.单个元素版本(1)返回一个pair,其成员pair :: first被设置为一个迭代器,指向新插入的元素或映射中具有等效键的元素。如果插入了新元素,则将pair中的pair :: second元素设置为true;如果已存在相同的键,则将该元素设置为false。

2.带有提示(2)的版本返回一个迭代器,指向新插入的元素或映射中已经具有相同键的元素。 成员类型迭代器是指向元素的双向迭代器类型

  /*make_pair内敛函数  返回一个pair对象*/
  //template<class K, class V>
  //inline pair<K, V> make_pair(const K& k, const V& v)
  //{ 
  //  return pair<K, V>( k,v);    
  //} 
  void test_map_insert( )
  {
      //实现一个字典
      typedef map<string, string>::iterator MyIterator;
      map<string, string> direct;
      direct.insert( pair<string, string>("insert", "插入" ));
      direct.insert( pair<string, string>("sort", "排序" ));
      direct.insert( make_pair("apple", "苹果" ));
      direct.insert( make_pair("insert", "插入" ));
      direct.insert( make_pair("insert", "插入" ));
      direct.insert( make_pair("sort", "排序" ));
  
      //(1)pair<iterator, bool> insert( const value_type& V); 
      pair<MyIterator, bool> ret = direct.insert(make_pair("apple", "苹果"));
      if(ret.second == false)  //插入元素已经存在,将second置为false
      {
          cout<<" 'apple' is inserted!";
          //first被设置为一个迭代器,指向新插入的元素或映射中具有等效键的元素
          cout<< "with value of "<<ret.first->second<<endl;
      }
      //(2)指定iterator插入
      direct.insert(direct.begin( ), make_pair("os1", "操作系统1"));
      direct.insert(direct.begin( ), make_pair("os2", "操作系统2")); //os2仍在os1后面
  
      //(3)范围插入
      map<string, string> another;
      another.insert(direct.begin( ), direct.end( ));
  
      //遍历(和set方式类似)
      MyIterator it = direct.begin( );
      while( it != direct.end( ))
      {
          cout<<it->first<<": "<<it->second<<endl;
          ++it;
      }
  }

(2)operator[]接口

原型如下:

map重载了“[]”运算符。重载的运算符“[]”实质上调用了前面中版本(1)的insert接口,它利用了insert的返回值(一个pair<iterator, bool>类型),最后返回pair中的迭代器所指元素value值的引用。如此,便可通过“[]” 来进行map的插入操作,与此同时,还可对新插入的元素(或插入元素在map已经存在的元素)的value值进行修改。重载[]具体实现如下:

(*((this->insert(make_pair(k,mapped_type()))).first)).second

 对它进行大致解析后,可将其修改为:

 template<class K, class V>
 typedef map<K, V>::iterator MyIterator;
 mapped_type& operator[](const K& k)
 {
     //mapped_type是V值(value)的默认值,value为int的话则默认为0
     pair<MyIterator, bool> ret = this->insert(make_pair(k, mapped_type()));
     return ret.first->second;  //或者 *(ret.first ).second;      ret.first是MyIterator迭代器,最后返回迭代器所指元素的second值(也即是value)的引用。
 }

例子

// accessing mapped values
#include <iostream>
#include <map>
#include <string>

int main ()
{
  std::map<char,std::string> mymap;

  mymap['a']="an element";
  mymap['b']="another element";
  mymap['c']=mymap['b'];  //插入key为‘c’的元素,随后将其对应value值修改。

  //key为'a'的元素已经插入,此时返回‘a’所对应value的值
  std::cout << "mymap['a'] is " << mymap['a'] << '\n'; 
  //key为'b'的元素已经插入,此时返回‘b’所对应value的值
  std::cout << "mymap['b'] is " << mymap['b'] << '\n'; 
  //key为'c'的元素已经插入,此时返回‘c’所对应value的值
  std::cout << "mymap['c'] is " << mymap['c'] << '\n';  
  //直接插入key为‘a’的元素,元素对应value值为默认的“”
  std::cout << "mymap['d'] is " << mymap['d'] << '\n';  
  std::cout << "mymap now contains " << mymap.size() << " elements.\n";

  return 0;
}
//结果
/*mymap['a'] is an element
  mymap['b'] is another element
  mymap['c'] is another element
  mymap['d'] is
  mymap now contains 4 elements*/

值得注意的是,通过"[]"操作,若能确保查找元素在map中存在,还可以用它进行查找操作。因为在执行“[]”操作的过程中,插入失败会返回与查找元素拥有相同key值的一个iterator。

(3)按自定义顺序排序

 通常map对传入的元素,默认是按元素中key值进行排序(即前面定义的Less<Key>),通过前面的map原型定义不难看出它同样支持按自定义的顺序进行比较排序。例如我们自定义的Stu的对象,就可按对象中的年龄来进行排序:

 struct Stu
 {
     string name;
     int age;
     Stu(const string& na="", int a = 0)
         :name( na), age(a) { };
 };
 //定置一个仿函数
 struct compare
 {
     bool operator( )(const Stu& l, const Stu& r)
     { return l.age < r.age;}
 };
 void test_insert_compare()
 {//按传入Stu对象的年龄排序
     multimap<Stu, int, compare>sortmap;
     Stu s1("jack", 18);
     sortmap.insert(make_pair(s1, 1));
     Stu s2( "mike", 20);
     sortmap.insert(make_pair(s2, 1));
     Stu s3( "zede", 19);
     sortmap.insert(make_pair(s3, 1));
 
     map<Stu,int, compare>::iterator it = sortmap.begin( );
     while( it != sortmap.end( ))
     {
         cout<<it->first.name<<": "<<it->first.age<<" --"<<it->second<<endl;
         ++it;
     }
    //结果:
    //jack: 18 --1
    //zede: 19 --1
    //mike: 20 --1

 }

小应用:据出现次数,统计前K项语言


 //定制一个仿函数
 typedef map<string, int>::iterator CountIte;
 struct compare
 {
     bool operator()(CountIte lhs, CountIte rhs){
         return lhs->second > rhs->second;
     }
 };
 void get_topK_gramar(const vector<string>& v, int k)
 {
     //统计vector中各种相同key出现的次数
     map<string, int> countMap;
     for( int i =0; i< v.size( ); ++i)
     {
     //  map<string, int>::iterator it = countMap.find(v[ i]);
     //  if(it != countMap.end( ))//countmap中存在v[ i] 
     //      ++it->second;
     //  else
     //      countMap.insert( make_pair(v[i], 1);
         countMap[v[i]]++;
     }
 
     //定置仿函数,以每种编程语言出现次数进行排序
     //注意:不能用set来排序,因为它会去重,即其会将具有相同value值的某种语言过滤掉
     multiset<CountIte, compare> sortSet;
     CountIte cit = countMap.begin( );
     while( cit != countMap.end( ))
     {
         sortSet.insert(cit);
         ++cit;
     }
 
     multiset<CountIte, compare>::iterator it1 =  sortSet.begin( );
     for(; it1 != sortSet.end( ); ++it1)
     {
         if( k--)
             cout<<(*it1)->first<<":"<<(*it1)->second<<endl;
     }
 }
 void test_map_question( )
 {
     vector<string> v;
     v.push_back("python" );
     v.push_back("PHP" );
     v.push_back("PHP" );
     v.push_back("PHP" );
     v.push_back("PHP" );
     v.push_back("Java" );
     v.push_back("PHP" );
     v.push_back("C/C++" );
     v.push_back("C/C++" );
     v.push_back("python" );
     v.push_back("Java" );
     v.push_back("Java" );
     //统计语言次数,或者前K种语言
     get_topK_gramar(v, 3);
 }

结果:

multimap


multimap和map的唯一差别是map中key必须是唯一的,而multimap中的key是可以重复的。由于不用再判断是否插入了相同key的元素,所以multimap的单个元素版本的insert的返回值不再是一个pair, 而是一个iterator。也正是如此,所以multimap也不再提供operator[]接口。

multimap和map的其它用法基本类似。

点击回顶部

unordered_map/unordered_multimap


在C++11中有新出4个关联式容器:unordered_map/unordered_set/unordered_multimap/unordered_multiset。

这4个关联式容器与map/multimap/set/multiset功能基本类似,最主要就是底层结构不同,使用场景不容。

如果需要得到一个有序序列,使用红黑树系列的关联式容器,如果需要更高的查询效率,使用以哈希表为底层的关联式容器。 

此处只列举unordered_map,其它用法类似可自行查阅 可参考cplusplus

 unordered_map底层实现是用哈希桶实现的:

定义原型

在cplusplus的解释:

无序映射是关联容器,用于存储由键值和映射值组合而成的元素,并允许基于键快速检索各个元素。

在unordered_map中,键值通常用于唯一标识元素,而映射值是与该键关联的内容的对象。键和映射值的类型可能不同。

在内部,unordered_map中的元素没有按照它们的键值或映射值的任何顺序排序,而是根据它们的散列值组织成桶以允许通过它们的键值直接快速访问单个元素(具有常数平均时间复杂度)。

unordered_map容器比映射容器更快地通过它们的键来访问各个元素,尽管它们通过其元素的子集进行范围迭代通常效率较低。

无序映射实现直接访问操作符(operator []),该操作符允许使用其键值作为参数直接访问映射值。

容器中的迭代器至少是前向迭代器。

关键词:无序的 快速的检索 达到的是更快的访问 但是子集的范围迭代效率低

相关操作


 1.插入遍历...

 typedef unordered_map<string, double>::iterator MyIte;
 void test_unordered_map( )
 { 
     unordered_map<string, double> umap;
     umap.insert(make_pair("苹果", 2.5));
     umap.insert(make_pair("香蕉", 3.0));
     umap.insert(make_pair("香蕉", 3.0));
     umap.insert(make_pair("西瓜", 1.5));
     umap.insert(make_pair("哈密瓜", 3.0));
     umap["榴莲"] = 4.0;
     MyIte it = umap.begin( );
     while( it != umap.end( ))
     { 
         cout<<it->first<<" :"<<it->second<<endl;
         ++it;
     }   
     cout<<"桶数量:"<<umap.bucket_count( )<<endl;
     cout<<"负载因子:"<<umap.load_factor( )<<endl;
    //结果:
    //榴莲 :4
    //苹果 :2.5
    //哈密瓜 :3
    //香蕉 :3
    //西瓜 :1.5
    //桶数量:11
    //负载因子:0.454545
 }

2.自定义比较

 struct Store
 {
     string name;
     string addr;
     Store(const string& na="", const string& ad= "")
         :name(na),addr( ad){ }
 
     bool operator==(const Store& s) const     //重载==支持等于比较
     {   
         return name == s.name && addr == s.addr; 
     }
 };
 struct hash_key    //定制返回哈希值的仿函数
 {
     //BKDRHash
     size_t operator()(const Store& s) const
     {
         size_t seed = 131; /* 31 131 1313 13131 131313 etc.. */
         size_t hash = 0;
         size_t i = 0;
         for( i = 0; i < s.name.size(); ++i)
         {
             hash = ( hash * seed)  + s.name[i];
         }
         return hash;
     }
 };
 
 typedef unordered_map<Store, int, hash_key>::iterator MyIte;
 void test_unordered_map( )
 {
     unordered_map<Store, int, hash_key> umap;
     Store s1("火锅店", "重庆");
     Store s2("凉皮店", "西安");
     Store s3("烤鸭店", "北京");
 
     umap.insert(make_pair(s1, 1));
     umap.insert(make_pair(s2, 1));
     umap[s3] = 1;
 
     MyIte it = umap.begin( );
     while( it != umap.end( ))
     {
         cout<<it->first.name<<":"<<it->second<<endl;
         ++it;
     }
 }

3. 性能测试

测试insert比较map和unordered_map性能差异

 typedef unordered_map<int, int>::iterator MyIte;
 void test_unordered_map( )
 {
     unordered_map<int, int> umap;
     map<int, int>mp;
     srand(time( NULL));
 
     const int N = 100000;
     vector<int> a;
     for(int i=0; i< N; ++i)
         a.push_back(rand()%N);
 
     clock_t begin1,end1;
     begin1 = clock();
     umap.rehash(100000);  //通过rehash设置哈希桶数量,进一步提高效率
     for(int i =0; i< N; ++i)
         umap[a[i]];
     end1 = clock( );
     
     clock_t begin2, end2;
     begin2 = clock( );
     for( int i =0; i< N; ++i)
         mp[a[i]];
     end2= clock( );
 
     cout<<"负载因子:"<<umap.load_factor()<<" "<<"桶数:"<<umap.bucket_count( )<<endl;
    //统计运行的毫秒差
     cout<<"unordered_map:"<<(end1-begin1)/1000<<endl;
     cout<<"map:"<<(end2-begin2)/1000<<endl;//统计运行的毫秒差
 }
//结果 普通
tp@tp:~$ g++ -std=c++11 1.cc   
tp@tp:~$ ./a.out 
负载因子:0.500463 桶数:126271
unordered_map:49
map:102

//rehash之后
负载因子:0.585883 桶数:107897
unordered_map:37
map:107

unordered_map 与 map之间差异比较(Linux平台下)

·map底层为红黑树查找大致为logN的时间复杂度;unordered_map底层是闭散列的哈希桶,查找为O(1),性能更优。

·调用insert操作,map相较于unordered_map操作慢,大致有2到3倍差异;但是map插入更加稳定

·unordered_map的erase操作会缩容,导致元素重新映射,降低性能。

·unordered_map要求传入的数据能够进行大小比较,“==”关系比较;所以自定义数据需要定置hash_value仿函数同时重载operator==。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_36670529/article/details/107038261

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签