持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
目录
map介绍:
map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
hash_map介绍:
hash_map基于hash table(哈希表)。 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。
其基本原理是:使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数,也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标,hash值)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素“分类”,然后将这个元素存储在相应“类”所对应的地方,称为桶。
但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。 总的来说,“直接定址”与“解决冲突”是哈希表的两大特点。
hash_map,首先分配一大片内存,形成许多桶。是利用hash函数,对key进行映射到不同区域(桶)进行保存。其插入过程是:
1、得到key
2、通过hash函数得到hash值
3、得到桶号(一般都为hash值对桶数求模)
4、存放key和value在桶内。
其取值过程是:
1、得到key
2、通过hash函数得到hash值
3、得到桶号(一般都为hash值对桶数求模)
4、比较桶的内部元素是否与key相等,若都不相等,则没有找到。
5、取出相等的记录的value。
hash_map中直接地址用hash函数生成,解决冲突,用比较函数解决。这里可以看出,如果每个桶内部只有一个元素,那么查找的时候只有一次比较。当许多桶内没有值时,许多查询就会更快了(指查不到的时候).
由此可见,要实现哈希表, 和用户相关的是:hash函数和比较函数。这两个参数刚好是我们在使用hash_map时需要指定的参数。
unordered_map介绍:
unordered_map也是一种关联容器,存储键值对,并且允许基于键快速检索单个元素。
在unordered_map中,键值通常用于唯一地标识元素,而映射值是与该键相关联的内容的对象。 键和映射值的类型可能不同。
在内部,unordered_map中的元素没有在任何特定的顺序排序对关键或映射值,但组织成桶取决于他们的散列值,以便快速访问单个元素直接的键值(平均一个恒定的平均时间复杂度)。
unordered_map容器通过键访问单个元素的速度比map容器快,尽管它们通常在通过元素子集进行范围迭代时效率较低。
无序映射实现了直接访问操作符(operator[]),允许使用键值作为参数直接访问映射值。
从C++11开始,哈希表实现已添加到C++标准库标准。决定对类使用备用名称,以防止与这些非标准实现的冲突,并防止在其代码中有hash_table的开发人员无意中使用新类。
所选择的备用名称是unordered_map,它更具描述性,因为它暗示了类的映射接口和其元素的无序性质。
可见
hash_map,unordered_map本质是一样的,只不过unordered_map被纳入了C++标准库标准。
接下来通过代码测试一下三种map的性能与内存使用情况,
测试代码:
/**
比较map、hash_map和unordered_map的执行效率以及内存占用情况
**/
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstring>
#include <map>
#include <ext/hash_map>
#include <tr1/unordered_map>
using namespace std;
using namespace __gnu_cxx;
using namespace std::tr1;
#define N 1000000 //分别测试N=100,000、N=1,000,000、N=10,000,000以及N=100,000,000
//分别定义MapKey=map<int,int>、hash_map<int,int>、unordered_map<int,int>
//typedef map<int,int> MapKey; //采用map
//typedef hash_map<int,int> MapKey; //采用hash_map
typedef unordered_map<int,int> MapKey; //采用unordered_map
int GetPidMem(pid_t pid,string& memsize)
{
char filename[1024];
snprintf(filename,sizeof(filename),"/proc/%d/status",pid);
ifstream fin;
fin.open(filename,ios::in);
if (! fin.is_open())
{
cout<<"open "<<filename<<" error!"<<endl;
return (-1);
}
char buf[1024];
char size[100];
char unit[100];
while(fin.getline(buf,sizeof(buf)-1))
{
if (0 != strncmp(buf,"VmRSS:",6))
continue;
sscanf(buf+6,"%s%s",size,unit);
memsize = string(size)+string(unit);
}
fin.close();
return 0;
}
int main(int argc, char *argv[])
{
struct timeval begin;
struct timeval end_insert_;
struct timeval end_find_;
struct timeval end_erase_;
MapKey MyMap;
gettimeofday(&begin,NULL);
for(int i=0;i<N;++i)
MyMap.insert(make_pair(i,i));
gettimeofday(&end_insert_,NULL);
cout<<"insert N="<<N<<",cost="<< (end_insert_.tv_sec-begin.tv_sec) * 1000000 + (end_insert_.tv_usec-begin.tv_usec)<<" usec"<<endl;
for(int i=0;i<N;++i)
MyMap.find(i);
gettimeofday(&end_find_,NULL);
cout<<"find all N="<<N<<",cost="<<(end_find_.tv_sec-end_insert_.tv_sec) * 1000000 + (end_find_.tv_usec-end_insert_.tv_usec) <<" usec"<<endl;
for(int i=0;i<N;++i)
MyMap.erase(i);
gettimeofday(&end_erase_,NULL);
cout<<"erase all N="<<N<<",cost="<<(end_erase_.tv_sec-end_find_.tv_sec) * 1000000 + (end_erase_.tv_usec-end_find_.tv_usec) <<" usec"<<endl;
string memsize;
GetPidMem(getpid(),memsize);
cout<<memsize<<endl;
return 0;
}
测试代码来源于Linux下map hash_map和unordered_map效率比较_whizchen的博客-CSDN博客_c++ unordered_map,但是测试结果不同,具体如下。
测试结果:
当 N = 1,000,000时:
| N = 1000000 | insert(us) | find(us) | erase(us) | 内存(KB) |
| map | 842139 | 440564 | 665866 | 49892 |
| hash_map | 183849 | 33469 | 69259 | 46564 |
| unordered_map | 187592 | 37369 | 68699 | 42416 |
当 N = 10,000,000时:
| N = 10000000 | insert(us) | find(us) | erase(us) | 内存(KB) |
| map | 10490558 | 5377150 | 7647703 | 471708 |
| hash_map | 1692442 | 312770 | 650502 | 413972 |
| unordered_map | 2016034 | 345627 | 668341 | 455756 |
当 N = 100,000,000时:
| N = 100000000 | insert(us) | find(us) | erase(us) | 内存(KB) |
| map | 124082466 | 70372560 | 91549272 | 4690512 |
| hash_map | 17488308 | 3532250 | 7044934 | 3914452 |
| unordered_map | 19799597 | 3519920 | 6829680 | 4302924 |
测试结论:
可见map效率最差无疑,但是hash_map与unordered_map在查找和删除时效率相差不大,当数据量逐渐增大时,hash_map的插入性能更好,所以标准库的无序列表性能上跟hash_map相差不大,但是略逊。
内存使用上,一直是map占用最多, unordered_map次之,hash_map占用内存最少。
参考文章:
1、Linux下map hash_map和unordered_map效率比较_whizchen的博客-CSDN博客_c++ unordered_map
2、STL map, hash_map , unordered_map区别、对比 - 云+社区 - 腾讯云