map,hash_map和unordered_map效率比较

Posted 程序员超时空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了map,hash_map和unordered_map效率比较相关的知识,希望对你有一定的参考价值。

转载地址:https://blog.csdn.net/whizchen/article/details/9286557

原理介绍

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 maps are associative containers that store elements formed by the combination of a_key value_and a_mapped value_, and which allows for fast retrieval of individual elements based on their keys.

In anunordered_map, the_key value_is generally used to uniquely identify the element, while the_mapped value_is an object with the content associated to this_key_. Types of_key_and_mapped_value may differ.

Internally, the elements in theunordered_mapare not sorted in any particular order with respect to either their_key_or_mapped_values, but organized into_buckets_depending on their hash values to allow for fast access to individual elements directly by their_key values_(with a constant average time complexity on average).

unordered_mapcontainers are faster thanmapcontainers to access individual elements by their_key_, although they are generally less efficient for range iteration through a subset of their elements.

Unordered maps implement the direct access operator (operator[]) which allows for direct access of the_mapped value_using its_key value_as argument.

unordered_map与map的区别

boost::unordered_map, 它与 stl::map的区别就是,stl::map是按照operator<比较判断元素是否相同,以及比较元素的大小,然后选择合适的位置插入到树中。所以,如果对map进行遍历(中序遍历)的话,输出的结果是有序的。顺序就是按照operator< 定义的大小排序。
而boost::unordered_map是计算元素的Hash值,根据Hash值判断元素是否相同。所以,对unordered_map进行遍历,结果是无序的。
用法的区别就是,stl::map 的key需要定义operator< 。 而boost::unordered_map需要定义hash_value函数并且重载operator==。对于内置类型,如string,这些都不用操心。对于自定义的类型做key,就需要自己重载operator< 或者hash_value()了。
最后,说,当不需要结果排好序时,最好用unordered_map。
其实,stl::map对于与java中的TreeMap,而boost::unordered_map对应于java中的HashMap。

测试代码

/**
比较map、hash_map和unordered_map的执行效率以及内存占用情况
**/
 
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>	
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <ext/hash_map>
#include <tr1/unordered_map>
 
using namespace std;
 
using namespace __gnu_cxx;
 
using namespace std::tr1;
 
#define N 100000000  //分别测试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;
		
	MapKey MyMap;
	
	gettimeofday(&begin,NULL);
	
	for(int i=0;i<N;++i)
		MyMap.insert(make_pair(i,i));
	
	gettimeofday(&end,NULL);
	
	cout<<"insert N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;
	
	for(int i=0;i<N;++i)
		MyMap.find(i);
 
	gettimeofday(&end,NULL);
	
	cout<<"insert and getall N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;
	
	string memsize;
	
	GetPidMem(getpid(),memsize);
	
	cout<<memsize<<endl;
	
	return 0;

运行结果

记录数N=100000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

0.110705

0.171859

5,780kB

hash_map

0.079074

0.091751

5,760kB

unordered_map

0.041311

0.050298

5,216kB

记录数N=1000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

1.22678

1.95435

47,960kB

hash_map

0.684772

0.814646

44,632kB

unordered_map

0.311155

0.386898

40,604kB

记录数N=10000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

14.9517

23.9928

469,844kB

hash_map

5.93318

7.18117

411,904kB

unordered_map

3.64201

4.43355

453,920kB

记录数N=100000000时,结果如下:

Map类型

插入耗时,单位秒

插入加遍历耗时,单位秒

内存占用情况

map

167.941

251.591

4,688,692kB

hash_map

46.3518

57.6972

3,912,632kB

unordered_map

28.359

35.122

4,3012,56kB

结果分析

运行效率方面:unordered_map最高,hash_map其次,而map效率最低

占用内存方面:hash_map内存占用最低,unordered_map其次,而map占用最高

以上是关于map,hash_map和unordered_map效率比较的主要内容,如果未能解决你的问题,请参考以下文章

详解STL中的map和hash_map区别

C++ 中的 map 与 hash_map

Hash_Map

Hash_Map

hash_map

VS2012关于hash_map的使用简略