Linux下map、hash_map和unordered_map效率比較

原理介紹

map介紹

Map是STL[1]的一個關聯容器,它提供一對一(其中第一個可以稱爲關鍵字,每個關鍵字只能在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 an unordered_map, the key value is generally used to uniquely identify the element, while the mapped valueis an object with the content associated to this key. Types of key and mapped value may differ.

Internally, the elements in the unordered_map are not sorted in any particular order with respect to either theirkey 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_map containers are faster than map containers 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 themapped 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。 

測試代碼

  1. /** 
  2. 比較map、hash_map和unordered_map的執行效率以及內存佔用情況 
  3. **/  
  4.   
  5. #include <sys/types.h>  
  6. #include <unistd.h>  
  7. #include <sys/time.h>   
  8. #include <iostream>  
  9. #include <fstream>  
  10. #include <string>  
  11. #include <map>  
  12. #include <ext/hash_map>  
  13. #include <tr1/unordered_map>  
  14.   
  15. using namespace std;  
  16.   
  17. using namespace __gnu_cxx;  
  18.   
  19. using namespace std::tr1;  
  20.   
  21. #define N 100000000  //分別測試N=100,000、N=1,000,000、N=10,000,000以及N=100,000,000  
  22.   
  23. //分別定義MapKey=map<int,int>、hash_map<int,int>、unordered_map<int,int>  
  24. //typedef map<int,int> MapKey;          //採用map  
  25. //typedef hash_map<int,int> MapKey;     //採用hash_map  
  26. typedef unordered_map<int,int> MapKey;  //採用unordered_map  
  27.   
  28.   
  29.   
  30. int GetPidMem(pid_t pid,string& memsize)  
  31. {  
  32.     char filename[1024];  
  33.       
  34.     snprintf(filename,sizeof(filename),"/proc/%d/status",pid);  
  35.       
  36.     ifstream fin;  
  37.       
  38.     fin.open(filename,ios::in);  
  39.     if (! fin.is_open())  
  40.     {  
  41.         cout<<"open "<<filename<<" error!"<<endl;  
  42.         return (-1);  
  43.     }  
  44.       
  45.     char buf[1024];  
  46.     char size[100];  
  47.     char unit[100];  
  48.       
  49.     while(fin.getline(buf,sizeof(buf)-1))  
  50.     {  
  51.         if (0 != strncmp(buf,"VmRSS:",6))  
  52.             continue;  
  53.           
  54.         sscanf(buf+6,"%s%s",size,unit);  
  55.           
  56.         memsize = string(size)+string(unit);  
  57.     }  
  58.       
  59.     fin.close();  
  60.       
  61.     return 0;  
  62. }  
  63.   
  64. int main(int argc, char *argv[])  
  65. {  
  66.     struct timeval begin;  
  67.       
  68.     struct timeval end;  
  69.           
  70.     MapKey MyMap;  
  71.       
  72.     gettimeofday(&begin,NULL);  
  73.       
  74.     for(int i=0;i<N;++i)  
  75.         MyMap.insert(make_pair(i,i));  
  76.       
  77.     gettimeofday(&end,NULL);  
  78.       
  79.     cout<<"insert N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;  
  80.       
  81.     for(int i=0;i<N;++i)  
  82.         MyMap.find(i);  
  83.   
  84.     gettimeofday(&end,NULL);  
  85.       
  86.     cout<<"insert and getall N="<<N<<",cost="<<end.tv_sec-begin.tv_sec + float(end.tv_usec-begin.tv_usec)/1000000<<" sec"<<endl;  
  87.       
  88.     string memsize;  
  89.       
  90.     GetPidMem(getpid(),memsize);  
  91.       
  92.     cout<<memsize<<endl;  
  93.       
  94.     return 0;  
  95. }  

運行結果

記錄數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佔用最高

參考:http://www.cplusplus.com/reference/unordered_map/unordered_map/

發佈了28 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章