概述
转自:http://blog.sina.com.cn/s/blog_59b6af690100xy0z.html
后半部分转自:http://blog.chinaunix.net/uid-20384806-id-3055333.html
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
下面举例说明什么是一对一的数据映射。比如一个班级中,每个学生的学号跟他的姓名就存在着一一映射的关系,这个模型用map可能轻易描述,很明显学号用int描述,姓名用字符串描述(本篇文章中不用char *来描述字符串,而是采用STL中string来描述),下面给出map描述代码:
Map<int, string> mapStudent;
1.
map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map:
Map<int, string> mapStudent;
2.
在构造map容器后,我们就可以往里面插入数据了。这里讲三种插入数据的方法:
第一种:用insert函数插入pair数据,下面举例说明(以下代码虽然是随手写的,应该可以在VC和GCC下编译通过,大家可以运行下看什么效果,在VC下请加入这条语句,屏蔽4786警告
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
{
}
}
第二种:用insert函数插入value_type数据,下面举例说明
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
{
}
}
第三种:用数组方式插入数据,下面举例说明
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
{
}
}
以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,当然了第一种和第二种在效果上是完成一样的,用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值,用程序说明
mapStudent.insert(map<int, string>::value_type (1, “student_one”));
mapStudent.insert(map<int, string>::value_type (1, “student_two”));
上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,程序如下
Pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(map<int, string>::value_type (1, “student_one”));
我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。
下面给出完成代码,演示插入成功与否问题
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
Pair<map<int, string>::iterator, bool> Insert_Pair;
{
}
}
大家可以用如下程序,看下用数组插入在数据覆盖上的效果
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
{
}
}
3.
在往map里面插入了数据,我们怎么知道当前已经插入了多少数据呢,可以用size函数,用法如下:
Int nSize = mapStudent.size();
4.
这里也提供三种方法,对map进行遍历
第一种:应用前向迭代器,上面举例程序中到处都是了,略过不表
第二种:应用反相迭代器,下面举例说明,要体会效果,请自个动手运行程序
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
{
}
}
第三种:用数组方式,程序说明如下
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
//此处有误,应该是 for(int nIndex = 1; nIndex <= nSize; nIndex++)
//by rainfish
{
}
}
5.
在这里我们将体会,map在数据插入时保证有序的好处。
要判定一个数据(关键字)是否在map中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的map基本用法。
这里给出三种数据查找方法
第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了
第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器,程序说明
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
if(iter != mapStudent.end())
{
}
Else
{
}
}
第三种:这个方法用来判定数据是否出现,是显得笨了点,但是,我打算在这里讲解
Lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器)
Upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器)
例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3
Equal_range函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字,程序说明
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
iter = mapStudent.lower_bound(2);
{
}
iter = mapStudent.lower_bound(3);
{
}
iter = mapStudent.upper_bound(2);
{
}
iter = mapStudent.upper_bound(3);
{
}
Pair<map<int, string>::iterator, map<int, string>::iterator> mapPair;
mapPair = mapStudent.equal_range(2);
if(mapPair.first == mapPair.second)
}
Else
{
Cout<<”Find”<<endl;
}
mapPair = mapStudent.equal_range(3);
if(mapPair.first == mapPair.second)
}
Else
{
Cout<<”Find”<<endl;
}
}
6.
清空map中的数据可以用clear()函数,判定map中是否有数据可以用empty()函数,它返回true则说明是空map
7.
这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法
#include <map>
#include <string>
#include <iostream>
Using namespace std;
Int main()
{
//如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好
}
8.
这里有swap,key_comp,value_comp,get_allocator等函数,感觉到这些函数在编程用的不是很多,略过不表,有兴趣的话可以自个研究
9.
这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int 型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过不去,下面给出两个方法解决这个问题
第一种:小于号重载,程序举例
#include <map>
#include <string>
Using namespace std;
Typedef struct tagStudentInfo
{
}StudentInfo, *PStudentInfo;
Int main()
{
mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));
for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)
}
以上程序是无法编译通过的,只要重载小于号,就OK了,如下:
Typedef struct tagStudentInfo
{
}StudentInfo, *PStudentInfo;
第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明
#include <map>
#include <string>
Using namespace std;
Typedef struct tagStudentInfo
{
}StudentInfo, *PStudentInfo;
Classs sort
{
};
Int main()
{
mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));
}
10.
由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,这里默认用的是小于号,即less<>,如果要从大到小排序呢,这里涉及到的东西很多,在此无法一一加以说明。
还要说明的是,map中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的,如果用map函数可以实现的功能,而STL
下面说下,map在空间上的特性,否则,估计你用起来会有时候表现的比较郁闷,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),我想大家应该知道,这些地方很费内存了吧,不说了……
关于头文件的说明
[转]http://biancheng.dnbcw.info/c/170128.html
标准std中只有map,是使用平衡二叉树实现的,查找和添加的复杂度都为O(log(n)),
没有提供hash map,gnu c++提供了hash_map,是一个hash map的实现,查找和添加复杂
度均为O(1)。
#include <ext/hash_map> #include <iostream> #include <cstring> using namespace std; using namespace __gnu_cxx; struct eqstr{ bool operator()(const char *s1, const char *s2)const{ return strcmp(s1,s2) == 0; } }; int main(){ hash_map<const char *,int,hash<const char *>,eqstr> months; months["january"] = 31; months["february"] = 28; months["march"] = 31; cout << "march -> " << months["march"] << endl; }
不过gnu hash_map和c++ stl的api不兼容,c++ tr1(C++ Technical Report
1)作为标准的扩展,实现了hash map,提供了和stl兼容一致的api,称为unorder_map.在头文件
<tr1/unordered_map>中。另外c++ tr1还提供了 正则表达式、智能指针、hash table、
随机数生成器的功能。
#include <iostream> #include <string> #include <tr1/unordered_map> using namespace std; int main(){ typedef std::tr1::unordered_map<int,string> hash_map; hash_map hm; hm.insert(std::pair<int,std::string>(0,"Hello")); hm[1] = "World"; for(hash_map::const_iterator it = hm.begin(); it != hm.end(); ++it){ cout << it->first << "-> " << it->second << endl; } return 0; }
map hash_map unordered_map 性能测试
by zieckey
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 16核
插入,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 241 | 2833 | 35888 | 381214 | 4439088 | 62233380 |
std::ext/hash_map | 97 | 1667 | 16466 | 146025 | 1788446 | 18512639 |
std::tr1::unordered_map | 77 | 772 | 8052 | 53094 | 658312 | 7429297 |
遍历,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 11 | 76 | 842 | 11603 | 155700 | 1771906 |
std::ext/hash_map | 47 | 430 | 4218 | 39880 | 470344 | 4781575 |
std::tr1::unordered_map | 1 | 1 | 2 | 1 | 2 | 1 |
查找,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 156 | 2111 | 30456 | 258709 | 4100260 | 59064394 |
std::ext/hash_map | 77 | 774 | 8056 | 56974 | 660231 | 7705527 |
std::tr1::unordered_map | 77 | 772 | 8051 | 54456 | 659537 | 7600263 |
删除,单位us | 100 | 1K | 10K | 100K | 1M | 10M |
std::map | 291 | 3641 | 49584 | 472414 | 6675897 | 92491113 |
std::ext/hash_map | 89 | 869 | 9068 | 86524 | 964767 | 10372650 |
std::tr1::unordered_map | 49 | 480 | 4879 | 33087 | 395098 | 4369617 |
- #include <iostream>
- #include <string>
- #include <sstream>
- #include <list>
- #include <map>
- #include <sys/time.h>
- #include <ext/hash_map>
- #include <tr1/unordered_map>
- namespace zl
- { //{{{
- struct equal_to
- {
- bool operator()(const char* s1,const char* s2)const
- {
- return strcmp(s1, s2)== 0;
- }
- };
- struct hash_string
- : public std::unary_function<std::string, std::size_t>
- {
- std::size_t
- operator()(const std::string& __s) const
- #ifdef __linux__
- { return std::tr1::Fnv_hash<>::hash(__s.data(), __s.length());}
- #else
- { return std::tr1::_Fnv_hash<>::hash(__s.data(), __s.length());}
- #endif
- };
- struct hash_charptr
- : public std::unary_function<const char*, std::size_t>
- {
- std::size_t
- operator()(const char* __s)const
- #ifdef __linux__
- { return std::tr1::Fnv_hash<>::hash(__s, strlen(__s));}
- #else
- { return std::tr1::_Fnv_hash<>::hash(__s, strlen(__s));}
- #endif
- };
- }//}}}
- typedef std::list<std::string> string_list;
- typedef std::map<std::string,int> string_map;
- typedef __gnu_cxx::hash_map<std::string,int, zl::hash_string> string_hash_map;
- typedef std::tr1::unordered_map<std::string,int> string_unordered_map;
- void fill_list(string_list& slist, size_t count);
- uint64_t current_usec();
- int main(int argc, char* argv[])
- {
- if (argc!= 2&& argc!= 3)
- {
- fprintf(stderr,"Usage:%s test_count rehashn", argv[0]);
- fprintf(stderr,"For example:%s 10000 rehashn", argv[0]);
- return -1;
- }
- size_t count = atoi(argv[1]);
- bool rehash = false;
- if (argc== 3)
- {
- rehash = true;
- }
- string_map smap;
- string_hash_map shash_map;
- string_unordered_map sunordered_map;
- if (rehash)
- {
- sunordered_map.rehash(count);
- }
- string_list slist;
- fill_list(slist, count);
- uint64_t start = 0;
- uint64_t end = 0;
- uint64_t map_insert_us = 0;
- uint64_t hash_map_insert_us = 0;
- uint64_t unordered_map_insert_us = 0;
- uint64_t map_traverse_us = 0;
- uint64_t hash_map_traverse_us = 0;
- uint64_t unordered_map_traverse_us = 0;
- uint64_t map_find_us = 0;
- uint64_t hash_map_find_us = 0;
- uint64_t unordered_map_find_us = 0;
- uint64_t map_delete_us = 0;
- uint64_t hash_map_delete_us = 0;
- uint64_t unordered_map_delete_us = 0;
- // Insert test
- {//{{{
- string_list::iterator it(slist.begin());
- string_list::iterator ite(slist.end());
- //map insert
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- smap[*it]= i;
- }
- end = current_usec();
- map_insert_us = end - start;
- //hash_map insert
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map[*it]= i;
- }
- end = current_usec();
- hash_map_insert_us = end - start;
- //unordered_map insert
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map[*it]= i;
- }
- end = current_usec();
- unordered_map_insert_us = end - start;
- }//}}}
- // Traverse test
- {//{{{
- //map traverse
- {
- string_map::iterator it(smap.begin());
- string_map::iterator ite(smap.end());
- start = current_usec();
- for (int i = 0; it != ite;++it)
- {
- i++;
- }
- end = current_usec();
- map_traverse_us = end - start;
- }
- //hash_map traverse
- {
- string_hash_map::iterator it(shash_map.begin());
- string_hash_map::iterator ite(shash_map.end());
- start = current_usec();
- for (int i = 0; it != ite;++it)
- {
- i++;
- }
- end = current_usec();
- hash_map_traverse_us = end - start;
- }
- //unordered_map traverse
- {
- string_unordered_map::iterator it(sunordered_map.begin());
- string_unordered_map::iterator ite(sunordered_map.end());
- start = current_usec();
- for (int i = 0; it != ite;++it)
- {
- i++;
- }
- end = current_usec();
- unordered_map_traverse_us = end - start;
- }
- }//}}}
- // Find test
- {//{{{
- string_list::iterator it(slist.begin());
- string_list::iterator ite(slist.end());
- //map find
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- smap[*it]= i;
- }
- end = current_usec();
- map_find_us = end - start;
- //hash_map find
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map[*it]= i;
- }
- end = current_usec();
- hash_map_find_us = end - start;
- //unordered_map find
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map[*it]= i;
- }
- end = current_usec();
- unordered_map_find_us = end - start;
- }//}}}
- // Delete test
- {//{{{
- string_list::iterator it(slist.begin());
- string_list::iterator ite(slist.end());
- //map delete
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- smap.erase(*it);
- }
- end = current_usec();
- map_delete_us = end - start;
- //hash_map delete
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map.erase(*it);
- }
- end = current_usec();
- hash_map_delete_us = end - start;
- //unordered_map delete
- it = slist.begin();
- start = current_usec();
- for (int i = 0; it != ite;++it,++i)
- {
- shash_map.erase(*it);
- }
- end = current_usec();
- unordered_map_delete_us = end - start;
- }//}}}
- //stat output
- std::cout<<" insert, count " << count << std::endl;
- std::cout<<" std::map " << map_insert_us << " usn";
- std::cout<<" std::ext/hash_map " << hash_map_insert_us << " usn";
- std::cout<<"std::tr1::unordered_map " << unordered_map_insert_us << " usn";
- std::cout<<"n";
- std::cout<<" traverse, count " << count << std::endl;
- std::cout<<" std::map " << map_traverse_us << " usn";
- std::cout<<" std::ext/hash_map " << hash_map_traverse_us << " usn";
- std::cout<<"std::tr1::unordered_map " << unordered_map_traverse_us << " usn";
- std::cout<<"n";
- std::cout<<" find, count " << count << std::endl;
- std::cout<<" std::map " << map_find_us << " usn";
- std::cout<<" std::ext/hash_map " << hash_map_find_us << " usn";
- std::cout<<"std::tr1::unordered_map " << unordered_map_find_us << " usn";
- std::cout<<"n";
- std::cout<<" delete, count " << count << std::endl;
- std::cout<<" std::map " << map_delete_us << " usn";
- std::cout<<" std::ext/hash_map " << hash_map_delete_us << " usn";
- std::cout<<"std::tr1::unordered_map " << unordered_map_delete_us << " usn";
- return 0;
- }
- void fill_list(string_list& slist, size_t count)
- {
- for (size_t i = 0; i< count;++i)
- {
- std::ostringstream oss;
- oss << i;
- //slist.push_back(MD5::getHexMD5(oss.str().c_str(), oss.str().length()));
- slist.push_back(oss.str());
- }
- }
- uint64_t current_usec()
- {
- struct timeval tv;
- gettimeofday( &tv, NULL );
- return tv.tv_sec * 1000 * 1000 + tv.tv_usec;
- }
最后
以上就是活泼萝莉为你收集整理的map, hash_map,unordered_map与性能测试的全部内容,希望文章能够帮你解决map, hash_map,unordered_map与性能测试所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复