c++中map、multimap、unordered_map、unordered_multimap的區別


前言:

c++的各種容器使用的時候很方便,但是如果作為一個初學者,看到一堆庫要記住也是很頭疼的,而且很多庫名稱會很相似,所以我們要很好的使用這些庫的時候,我們需要了解清楚它們底層實現的原理,這樣我們使用中就更加得心應手。

今天給大家分享一下mapmultimapunordered_mapunordered_multimap,看上去是不是很相似,今天就來描述幾者的區別。

作者:良知猶存

轉載授權以及圍觀:歡迎關注微信公眾號:羽林君

或者添加作者個人微信:become_me


幾種容器的初步介紹:

map簡介

map是STL的一個關聯容器,map 容器中所有的元素都會根據元素對應的鍵值來排序,而鍵值key 是唯一值,並不會出現同樣的鍵值key,也就是說假設已經有一個鍵值key 存在map 里,當同樣的鍵值key 再insert 資料時,新的資料就會覆蓋掉原本key 的資料。

map 的實作方式通常是用紅黑樹(red-black tree)實作的,這樣它可以保證可以在O(log n)時間內完成搜尋、插入、刪除,n為元素的數目。

常用函數

函數名 作用
begin 返回一個指向集合中第一個元素的迭代器
end 返回指向末尾的迭代器
empty 如果set為空,則返回true
size 返回集合中元素的數量
insert 在集合中插入元素
erase 從集合中擦除元素
swap 交換集合的內容
clear 刪除集合中的所有元素
emplace 構造新元素並將其插入到集合中
find 搜索具有給定鍵的元素
count 獲取與給定鍵匹配的元素數

注意: 這里map的鍵值 key是不可以重復的,在實際使用中,如果用map[key] = xxx,其中key已經存在的情況下,key對應的元素會被覆蓋掉,如果是用insert方式進行覆蓋則會失敗。

下面代碼里面用到了 first和second

map.first:第一個稱為(key)鍵值

map.second:第二個稱為(key)鍵值對應的數值(value)

#include <iostream>
#include <map>
using namespace std;
int main()
{
    std::map<int, std::string> studentMap = {
    {1, "Tom"},
    {7, "Mali"},
    {15, "John"}};

    for(auto i:studentMap2)
    {
        cout<<i.first<<" "
            <<i.second;
        cout<<endl;   
    }
    std::pair<std::map<int, std::string>::iterator, bool> retPair;
    retPair = studentMap.insert(std::pair<int, std::string>(15, "Bob"));  
    
    for(auto i:studentMap)
    {
        cout<<i.first<<" "
            <<i.second;
        cout<<endl;   
    }   

    std::map<int, std::string>::iterator itor = studentMap.find(7);
    if (itor != studentMap.end())
    {
        // cout<<itor->first<<" "
        //     <<itor->second;
        // cout<<endl;  
    }

    studentMap[15] = "Lily";

    for(auto i:studentMap)
    {
        cout<<i.first<<" "
            <<i.second;
        cout<<endl;   
    }    
    
    cout<<endl;   
    studentMap.erase(15);
    for(auto i:studentMap)
    {
        cout<<i.first<<" "
            <<i.second;
        cout<<endl;   
    }    
}

輸出結果如圖所示:

multimap簡介

multimaps是關聯式容器,它按照特定的順序,存儲由key和value映射成的鍵值對<key, value>,其中多個鍵值對之間的key是可以重復的,multimap在底層用二叉搜索樹(紅黑樹)來實現。

在內部,multimap中的元素總是通過其內部比較對象,按照指定的特定嚴格弱排序標准對key進行排序的。和map最大的區別,multimap中的key是可以重復的。

常用函數參照map函數使用

#include <iostream>
#include <map>
using namespace std;
int main()
{
    std::multimap<string, std::string> studentMap2 = {
    {"first", "Tom"},
    {"second", "Mali"},
    {"third", "John"}};    


    studentMap2.insert(std::pair<std::string, std::string>("first", "Bob"));  
    
    cout<< "output:"<<endl;  
    for (std::multimap<std::string, std::string>::iterator it = studentMap2.begin(); it != studentMap2.end(); it++) {
        std::cout << (*it).first << ", " << (*it).second << "\n";
    }

    std::multimap<std::string, std::string>::iterator itor_begin = studentMap2.lower_bound("first");
    std::multimap<std::string, std::string>::iterator itor_end   = studentMap2.upper_bound("first");
    while(itor_begin != itor_end) 
    {
        cout << itor_begin->first<<" "<< itor_begin++->second << endl;
        // cout << itor_begin->first<<" "<< itor_begin->second << endl;
        // itor_begin++;
    }

    cout << endl;
    cout << endl;

    std::cout << studentMap2.count("first") <<std::endl; // 輸出為2

}

以上代碼可以進行debug 進行查看,一個鍵值對應多個元素,對應數據排列的情況如下:

沖突的元素會使用元素鏈接的方式保存。


注意: multimap中沒有重載operator[]操作。

所以我們無法使用 multimap[key]進行訪問數據,是因為multimap的key可以對應多個數據,所以下標訪問是沒有意義的。


unordered_map簡介

unordered_map 內部實現了一個哈希表(也叫散列表,通過把關鍵碼值映射到Hash表中一個位置來訪問記錄,查找的時間復雜度可達到O(1),其在海量數據處理中有着廣泛應用)。因此,其元素的排列順序是無序的。容器中每個元素都是 key/value,每個 key 僅可出現一次

unordered_map 特點就是搜尋效率高,利用鍵值與哈希函數(hash function)計算出哈希值而快速的查找到對應的元素,時間復雜度為常數級別O(1), 而額外空間復雜度則要高出許多。unordered_map 與map 的使用方法基本上一樣,都是key/value 之間的映射,只是他們內部采用的資料結構不一樣。所以對於需要高效率查詢的情況可以使用unordered_map 容器。而如果對記憶體消耗比較敏感或者資料存放要求排序的話則可以用map 容器。

常用函數參照map函數使用

#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
   std::unordered_map < std::string , int > studentUMap3 = { 
        { "Tom" , 1 }, 
        { "Ann" , 4 }, 
        { "Job" , 2 } 
    };

   studentUMap3.insert(std::pair<std::string, int>("Job", 5));  
    

    cout<< "output:"<<endl;     
    for (auto it = studentUMap3.begin(); it != studentUMap3.end(); it++) {
        std::cout << (*it).first << ", " << (*it).second << "\n";
    }

    cout<<endl;     
    studentUMap3["Job"] = 3;
    for (auto it = studentUMap3.begin(); it != studentUMap3.end(); it++) {
        std::cout << (*it).first << ", " << (*it).second << "\n";
    }
}

unordered_map用法和map基本一致,但是考慮的訪問的速度有要求的情況下,我們優先使用,unordered_map;要是考慮到空間大小對程序影響的時候,我們優先使用map。

unordered_multimap簡介

unordered_multimap 是一個封裝哈希表的無序容器。容器中每個元素都是 key/value,每個 key 可重復出現

同map和unordered_map區別一樣,multimap通過key訪問單個元素的速度通常也比unordered_multimap容器慢。
常用函數參照map函數使用

#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
   std::unordered_multimap < int , int > studentUMap4;
    studentUMap4.insert(std::pair<int, int>(1, 333));
    studentUMap4.insert(std::pair<int, int>(3, 555));
    studentUMap4.insert(std::pair<int, int>(5, 666));


   studentUMap4.insert(std::pair<int, int>(5, 5));  
    

    cout<< "output:"<<endl;     
    for (auto it = studentUMap4.begin(); it != studentUMap4.end(); it++) {
        std::cout << (*it).first << ", " << (*it).second << "\n";
    }

    std::cout <<"count:"<< studentUMap4.count(5) <<std::endl;
    std::cout <<"size: "<< studentUMap4.size() <<std::endl;
    std::cout <<"empty?"<< studentUMap4.empty()<<"\n" <<std::endl;


    std::unordered_multimap < int , int >studentUMap5;
    studentUMap5.swap(studentUMap4);
    std::cout <<"count:"<< studentUMap4.count(5) <<std::endl;
    std::cout <<"size: "<< studentUMap4.size() <<std::endl;
    std::cout <<"empty?"<< studentUMap4.empty()<<"\n" <<std::endl;


    std::cout <<"count:" <<studentUMap5.count(5)<<"\n" <<std::endl;


    studentUMap5.clear();
    std::cout <<"size: " <<studentUMap5.size() <<std::endl;
    std::cout <<"empty?"<<studentUMap5.empty() <<std::endl;
}

重復元素對應的debug顯示

代碼執行輸出顯示

區別介紹

首先這幾個容器都是關聯容器,其中無論是有序關聯容器還是有序關聯容器都是基於(set:集合 key map:映射表 [key : value])

來自wiki


關聯容器是指C++標准模板庫中的一套類模板,實現了有序關聯數組。可用於存放任意數據類型的元素。C++標准中定義的關聯容器有: set, map, multiset, multimap。

關聯容器類似於C++中的無序關聯容器。差別為:

  • 關聯容器是紅黑樹實現,無序關聯容器是哈希表實現。
  • 關聯容器保證按鍵值有序遍歷,因此可以做范圍查找,而無序關聯容器不可以。
  • 關聯支持一些導航類的>>操作,如求出給定鍵最鄰近的鍵,最大鍵、最小鍵操作。
  • 關聯容器的迭代器不會失效,除非所指元素被刪除。
  • 無序關聯容器的iterator在修改元素時可能會失>>效。所以對關聯容器的遍歷與修改在一定程度上可並行
  • 哈希表查找時候要算hash,這個最壞時間復雜度是O(key的長度);基於比較的有序關聯容器通常只使用>頭幾個字符進行比較

關聯容器不支持順序容器的位置相關的操作,例如 push_front 或 push_back 操作。原因是關聯容器中>的元素是根據關鍵字來存儲的,這些操作對於關聯容器沒有意義。

關聯容器也不支持構造函數或插入操作這些接收一個元素值和一個數量值的操作。

因為map和multimap unordered_map和unordered_multimap兩兩區分時候,從底層實現來說,區別不是很大,所以我們先進行map與multimap的區別,再進行map和unordered_map區別比較。其他的類比即可。

最直觀的區別:

map和unordered_map;//key不允許重復,是單重映射表

multimap和unordered_multimap;//key允許重復,是多重映射表

實現機制的區別:

map和unordered_map的區別:內部實現機理不同

  • map :map內部實現了一個紅黑樹(紅黑樹是非嚴格平衡二叉搜索樹,而AVL是嚴格平衡二叉搜索樹),紅黑樹具有自動排序的功能,因此map內部的所有元素都是有序的,紅黑樹的每一個節點都代表着map的一個元素。因此,對於map進行的查找、刪除,添加等一系列的操作都相當於是對紅黑樹進行的操作。map中的元素是按照二叉搜索樹 (又名兒茶查找樹、二叉排序樹–特點就是左子樹上所有節點的鍵值都小於根節點的鍵值,右子樹所有節點的鍵值都大於根結點的鍵值)存儲的,使用中序遍歷可將鍵值按照從小到大遍歷出來。
    map和multimap的區別在於,map不允許相同key值存在,multimap則允許相同的key值存在。

  • unordered_map :unordered_map內部實現了一個哈希表 (也叫散列表,通過把關鍵碼值映射到Hash表中一個位置來訪問記錄,查找的時間復雜度可達到O(1),其在海量數據處理中有着廣泛應用)。因此,其元素的排列順序都是無序的。unordered_map 和 unordered_multimap 都是基於哈希表實現的,而且沖突策略采用的是鏈地址法。

使用的角度

空間占用率和效率上

unorder_map占用的內存更加高一點,unorder_map內部采用hash表,map內部采用的是紅黑樹,內存占有率的問題轉化成hash表 VS 紅黑樹,還是unorder_map內存要高一點.

但是unorder_map采用哈希表搜索,搜尋效率高,利用鍵值與哈希函數(hash function)計算出哈希值而快速的查找到對應的元素,時間復雜度為常數級別O(1)

在關鍵字類型的元素沒有明顯的序的情況下,或者在某些應用中,維護元素有序的代價非常高昂,采用無序容器代替map來說效果會好。

結語

這就是我對mapmultimapunordered_mapunordered_multimap幾者的分享,有機會可以給大家一起分享一下set, multisetunordered_set, unordered_multiset的使用與區別。如果大家有更好的想法和需求,也歡迎大家加我好友交流分享哈。


作者:良知猶存,白天努力工作,晚上原創公號號主。公眾號內容除了技術還有些人生感悟,一個認真輸出內容的職場老司機,也是一個技術之外豐富生活的人,攝影、音樂 and 籃球。關注我,與我一起同行。

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推薦閱讀

【1】在球場上我向人民幣玩家低了頭

【2】Linux開發coredump文件分析實戰分享

【3】CPU中的程序是怎么運行起來的 必讀

【4】cartographer環境建立以及建圖測試

【5】設計模式之簡單工廠模式、工廠模式、抽象工廠模式的對比

本公眾號全部原創干貨已整理成一個目錄,回復[ 資源 ]即可獲得。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM