map/unordered_map原理和使用整理

結論

新版的hash_map都是unordered_map了,這裏只說unordered_map和map.

運行效率方面:unordered_map最高,而map效率較低但 提供了穩定效率和有序的序列。

佔用內存方面:map內存佔用略低,unordered_map內存佔用略高,而且是線性成比例的。

需要無序容器,快速查找刪除,不擔心略高的內存時用unordered_map;有序容器穩定查找刪除效率,內存很在意時候用map。

原理

map的內部實現是二叉平衡樹(紅黑樹);hash_map內部是一個hash_table一般是由一個大vector,vector元素節點可掛接鏈表來解決衝突來實現.

這裏寫圖片描述

hash_map其插入過程是:

  1. 得到key
  2. 通過hash函數得到hash值
  3. 得到桶號(一般都爲hash值對桶數求模)
  4. 存放key和value在桶內。

其取值過程是:

  1. 得到key
  2. 通過hash函數得到hash值
  3. 得到桶號(一般都爲hash值對桶數求模)
  4. 比較桶的內部元素是否與key相等,若都不相等,則沒有找到。
  5. 取出相等的記錄的value。

hash_map中直接地址用hash函數生成,解決衝突,用比較函數解決。

性能特點

非頻繁的查詢用map比較穩定;頻繁的查詢用hash_map效率會高一些,c++11中的unordered_map查詢效率會更高一些但是內存佔用比hash_map稍微大點。unordered_map 就是 boost 裏面的 hash_map 實現。

其實,stl::map對於與java中的TreeMap,而boost::unordered_map對應於java中的HashMap。python中的map就是hashmap實現的,所以查詢效率會比C++的map查詢快。(java,python官方版的虛擬機都是用C語言實現的,所以內部的思想和方法都是通用的。)

若考慮有序,查詢速度穩定,容器元素量少於1000,非頻繁查詢那麼考慮使用map。

若非常高頻查詢(100個元素以上,unordered_map都會比map快),內部元素可非有序,數據大超過1k甚至幾十萬上百萬時候就要考慮使用unordered_map(元素上千萬上億時4GB的內存就要擔心內存不足了,需要數據庫存儲過程挪動到磁盤中)。

hash_map相比unordered_map就是千萬級別以上內存佔用少15MB,上億時候內存佔用少300MB,百萬以下都是unordered_map佔用內存少,
且unordered_map插入刪除相比hash_map都快一倍,查找效率相比hash_map差不多,或者只快了一點約1/50到1/100。
綜合非有序或者要求穩定用map,都應該使用unordered_map,set類型也是類似的。

unordered_map 查找效率快五倍,插入更快,節省一定內存。如果沒有必要排序的話,儘量使用 hash_map(unordered_map 就是 boost 裏面的 hash_map 實現)

使用unordered_map

unordered_map需要重載hash_value函數,並重載operator ==運算符。

參考鏈接:STL map與Boost unordered_map

使用Hash_map需要注意的問題

/*
 *\author peakflys
 *\brief 演示hash_map鍵值更改造成的問題
 */
#include <iostream>
#include <ext/hash_map>

struct Unit
{
    char name[32];
    unsigned int score;
    Unit(const char *_name,const unsigned int _score) : score(_score)
    {   
        strncpy(name,_name,32);
    }   
};
int main()
{
    typedef __gnu_cxx::hash_map<char*,Unit*> uHMap;
    typedef uHMap::value_type hmType;
    typedef uHMap::iterator hmIter;

    uHMap hMap;
    Unit *unit1 = new Unit("peak",100);
    Unit *unit2 = new Unit("Joey",20);
    Unit *unit3 = new Unit("Rachel",40);

    hMap[unit1->name] = unit1;
    hMap[unit2->name] = unit2;

    hMap.insert(hmType(unit3->name,unit3));
    hMap.insert(hmType(unit4->name,unit4));
    for(hmIter it=hMap.begin();it!=hMap.end();++it)
    {   
        std::cout<<it->first<<"\t"<<it->second->score<<std::endl;//正常操作
    }  

    for(hmIter it=hMap.begin();it!=hMap.end();++it)
    {
        Unit *unit = it->second;
        //hMap.erase(it++);
        delete unit; 
        //delete釋放節點內存,但是hMap沒有除去,造成hMap內部錯亂,有可能宕機
    } 

    hmIter it = hMap.begin();
    strncpy(it->first,"cc",32);//強行更改

    for(hmIter it=hMap.begin();it!=hMap.end();++it)
    {   
        std::cout<<it->first<<"\t"<<it->second->score<<std::endl;
        //死循環,原因參加上面++操作說明
        /*operator++ 操作是從_M_cur開始,優先_M_cur->_M_next,爲空時遍歷vector直至找到一個_M_cur不爲空的節點,遍歷vector 時需要取它對應的桶位置(參砍上面hash_map取值過程),_M_bkt_num_key(key)中key的值是修改後的值,假如你改的鍵值,通過 此函數得到的桶位置在你當前元素之前,這樣就造成了死循環.
        */
    }   
    return 0;
} 

參考鏈接:

map hash_map unordered_map 性能測試
Linux下map hash_map和unordered_map效率比較
關於hash_map的一點感悟
C++ STL之哈希表 | unordered_map

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章