C++ STL 系列——無序容器(unordered_map、unordered_multimap、unordered_set、unordered_multiset)


一、什么是無序容器

無序容器是 C++ 11 標准正式引入到 STL 標准庫中的,和關聯式容器一樣,無序容器也使用鍵值對的方式存儲數據,不過關聯式容器底層采用紅黑樹,無序容器底層采用哈希表。

C++ STL 底層采用哈希表實現無序容器時,會將所有數據存儲到一整塊連續的內存空間中,並且當數據存儲位置發生沖突時,解決方法選用的是“鏈地址法”(又稱“開鏈法”)。

無序容器特點:

  • 無序容器內部存儲的鍵值對是無序的,各鍵值對的存儲位置取決於該鍵值對中的鍵
  • 和關聯式容器相比,無序容器擅長通過指定鍵查找對應的值(平均時間復雜度為 O(1));但對於使用迭代器遍歷容器中存儲的元素,無序容器的執行效率則不如關聯式容器。

1.1 無序容器種類

無序容器 功能
unordered_map 存儲鍵值對 <key, value> 類型的元素,其中各個鍵值對鍵的值不允許重復,且該容器中存儲的鍵值對是無序的。
unordered_multimap 和 unordered_map 唯一的區別在於,該容器允許存儲多個鍵相同的鍵值對。
unordered_set 不再以鍵值對的形式存儲數據,而是直接存儲數據元素本身(當然也可以理解為,該容器存儲的全部都是鍵key 和值 value 相等的鍵值對,正因為它們相等,因此只存儲 value 即可)。另外,該容器存儲的元素不能重復,且容器內部存儲的元素也是無序的。
unordered_multiset 和 unordered_set 唯一的區別在於,該容器允許存儲值相同的元素。

與有序容器僅有一個區別,無序容器是不會對存儲的鍵值對排序。實際場景中如果涉及大量遍歷容器的操作,建議首選關聯式容器;反之,如果更多的操作是通過鍵獲取對應的值,則應首選無序容器。

1.2 自定義無序容器的哈希函數和比較規則

每種無序容器都指定了默認的 hash<key> 哈希函數和 equal_to<key> 比較規則,但它們僅適用於基本類型。

自定義哈希函數

哈希函數只是一個稱謂,其本體並不是普通的函數形式,而是一個函數對象類。因此,如果我們想自定義個哈希函數,就需要自定義一個函數對象類。

class Person{
public:
    Person(string name, int age):name(name),age(age){};
    string getName() const;
    int getAge() const;

private:
    string name;
    int age;
};

string Person::getName() const
    return this->name;

int Person::getAge() const
    return this->age;

如果需要創建一個可存儲 Person 類對象的 unordered_set 容器,需要以函數對象類的方式自定義一個哈希函數:

class hash_fun(){
public:
    int operator()(const Person &A) const
        return A.getAge();
};
  • 注意,重載 ( ) 運算符時,其參數必須為 const 類型,且該方法也必須用 const 修飾。
  • 利用 hash_fun 函數對象類的 () 運算符重載方法,自定義了適用於 Person 類對象的哈希函數。該哈希函數每接收一個 Person 類對象,都會返回該對象的 age 成員變量的值。
std::unordered_set<Person, hash_fun> myset;

自定義比較規則

默認情況下無序容器使用的 std::equal_to<key> 比較規則,其本質也是一個函數對象類,底層實現如下:

template<class T>
class equal_to{
public:
    bool operator()(const T& _Left, const T& _Right) const
        return (_Left == _Right);
};

對於自定義的類型,實現自定義比較規則有兩種方法:

  1. 在自定義類中重載 == 運算符,這會使在自定義類的 std::equal_to<key> 中使用 == 運算符合法。
  2. 以函數對象類的方式,自定義一個適用於 myset 容器的比較規則

重載 == 運算符

// 在 Person 類外部添加,重載 == 
bool operator==(const Person &A, const Person &B)
    return (A.getAge() == B.getAge());

重載 == 運算符后,還是以默認的 std::equal_to<key> 函數作為比較規則

std::unordered_set<Person, hash_fun> myset{{"zhangsan", 40},{"zhangsan", 40},{"lisi", 40},{"lisi", 30}};
// 只會存儲 {"zhangsan", 40} 和 {"lisi", 30}

以函數對象類的方式自定義比較規則

class mycmp{
public:
    bool operator()(const Person &A, const Person &B) const
        return ( A.getName() == B.getName()) && (A.getAge() == B.getAge());
};

// 創建 myset 容器
std::unordered_set<Person, hash_fun, mycmp> myset{ {"zhangsan", 40},{"zhangsan", 40},{"lisi", 40},{"lisi", 30} };

完整代碼:

#include <iostream>
#include <string>
#include <unordered_set>

using namespace std;

class Person{
public:
    Person(string name, int age):name(name), age(age){};
    string getName() const;
    int ageAge() const;
private:
    string name;
    int age;
};

string Person::getName() const
    return this->name;

int Person::getAge() const
    return this->age;

// 自定義哈希函數
class hash_fun{
public:
    int operator()(const Person &A) const
        return A.getAge();
};

// 重載 ==  運算符,容器繼續使用默認 equal_to<key> 規則
bool operator==(const Person &A, const Person &B)
    return (A.getAge() == B.getAge());

// 完全自定義比較規則,棄用 equal_to<key>
class mycmp{
public:
    bool operator()(const Person &A, const Person &B) const
        return (A.getName() == B.getName()) && (A.getAge() == B.getAge());
};

int main(){
    //使用自定義的 hash_fun 哈希函數,比較規則仍選擇默認的 equal_to<key>,前提是必須重載 == 運算符 
    std::unordered_set<Person, hash_fun> myset1{ {"zhangsan", 40},{"zhangsan", 40},{"lisi", 40},{"lisi", 30} }; 
    
    //使用自定義的 hash_fun 哈希函數,以及自定義的 mycmp 比較規則 
    std::unordered_set<Person, hash_fun, mycmp> myset2{ {"zhangsan", 40},{"zhangsan", 40},{"lisi", 40},{"lisi", 30} };
    
    cout << "myset1:" << endl; 
    for (auto iter = myset1.begin(); iter != myset1.end(); ++iter) 
        cout << iter->getName() << " " << iter->getAge() << endl;      // zhangsan 40         lisi 30
    
    cout << "myset2:" << endl; 
    for (auto iter = myset2.begin(); iter != myset2.end(); ++iter) 
        cout << iter->getName() << " " << iter->getAge() << endl;      // lisi 40    zhangsan 40    lisi 30
    return 0;
}

1.3 容器選擇

C++ STL 標准庫(以 C++ 11 為准)提供了以下幾種容器供我們選擇:

  1. 序列式容器:array、vector、deque、list 和 forward_list;
  2. 關聯式容器:map、multimap、set 和 multiset;
  3. 無序關聯式容器:unordered_map、unordered_multimap、unordered_set 和 unordered_multiset;
  4. 容器適配器:stack、queue 和 priority_queue。
  • 采用連續的存儲空間:array、vector、deque(一段一段連續空間);
  • 采用分散的存儲空間:list、forward_list 以及所有的關聯式容器和哈希容器。

選擇容器需要考慮的一些因素:

因素 序列容器 關聯容器 哈希容器 連續 分散
是否需要在容器的指定位置插入新元素?
是否對容器中各元素的存儲位置有要求?
是否需要使用指定類型的迭代器?(隨機訪問/雙向/前向)
當發生新元素的插入或刪除操作時,是否要避免移動容器中的其它元素? 是,避免
容器中查找元素的效率是否為關鍵的考慮因素?

二、unordered_map 容器

unordered_map 容器和 map 容器僅有一點不同,即 map 容器中存儲的數據是有序的,而 unordered_map 容器中是無序的。

unordered_map 容器模板的定義:

template < class Key,               // 鍵值對中鍵的類型
           class T,                 // 鍵值對中值的類型
           class Hash = hash<Key>,  //容器內部存儲鍵值對所用的哈希函數
           class Pred = equal_to<Key>,  // 判斷各個鍵值對鍵相同的規則
           class Alloc = allocator< pair<const Key,T> >  // 指定分配器對象的類型
           > class unordered_map;
參數 含義
<key,T> 前 2 個參數分別用於確定鍵值對中鍵和值的類型,也就是存儲鍵值對的類型。
Hash = hash 用於指明容器在存儲各個鍵值對時要使用的哈希函數,默認使用 STL 標准庫提供的 hash 哈希函數。注 意,默認哈希函數只適用於基本數據類型(包括 string 類型),而不適用於自定義的結構體或者類。
Pred = equal_to unordered_map 容器中存儲的鍵不能相等,判斷是否相等的規則,就由此參數指定。默認情況下,使用 STL 標准庫中提供的 equal_to 規則,該規則僅支持可直接用 == 運算符做比較的數據類型。

2.1 創建 unordered_map 容器

  • 默認構造函數
    std::unordered_map<std::string, std::string> umap;
    
  • 創建的同時初始化
    std::unordered_map<std::string, std::string> umap{
          {"Python 教程","http://c.biancheng.net/python/"},
          {"Java 教程","http://c.biancheng.net/java/"},
          {"Linux 教程","http://c.biancheng.net/linux/"} };
    
  • 拷貝/移動構造函數
    // 拷貝構造函數
    std::unordered_map<std::string, std::string> umap2(umap);
    
    // 移動構造函數
    // 返回臨時 unordered_map 容器的函數
    std::unordered_map <std::string, std::string > retUmap(){
          std::unordered_map<std::string, std::string>tempUmap{
              {"Python 教程","http://c.biancheng.net/python/"},
              {"Java 教程","http://c.biancheng.net/java/"},
              {"Linux 教程","http://c.biancheng.net/linux/"} };
          return tempUmap;
    }
    
    // 調用移動構造函數,創建 umap2 容器
    std::unordered_map<std::string, std::string> umap2(retUmap());
    
  • 選擇已有容器部分區域創建
    //傳入 2 個迭代器,
    std::unordered_map<std::string, std::string> umap2(++umap.begin(),umap.end());
    

2.2 成員方法

成員方法 功能
begin() 返回指向容器中第一個鍵值對的正向迭代器。
end() 返回指向容器中最后一個鍵值對之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不過在其基礎上增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
cend() 和 end() 功能相同,只不過在其基礎上,增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
empty() 若容器為空,則返回 true;否則 false。
size() 返回當前容器中存有鍵值對的個數。
max_size() 返回容器所能容納鍵值對的最大個數,不同的操作系統,其返回值亦不相同。
operator[key] 該模板類中重載了 [] 運算符,其功能是可以向訪問數組中元素那樣,只要給定某個鍵值對的鍵 key,就可以獲取該鍵對應的值。注意,如果當前容器中沒有以 key 為鍵的鍵值對,則其會使用該鍵向當前容器中插入一個新鍵值對。
at(key) 返回容器中存儲的鍵 key 對應的值,如果 key 不存在,則會拋出 out_of_range 異常。
find(key) 查找以 key 為鍵的鍵值對,如果找到,則返回一個指向該鍵值對的正向迭代器;反之,則返回一個指向容器中最后一個鍵值對之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找以 key 鍵的鍵值對的個數。
equal_range(key) 返回一個 pair 對象,其包含 2 個迭代器,用於表明當前容器中鍵為 key 的鍵值對所在的范圍。
emplace() 向容器中添加新鍵值對,效率比 insert() 方法高。
emplace_hint() 向容器中添加新鍵值對,效率比 insert() 方法高。
insert() 向容器中添加新鍵值對。
erase() 刪除指定鍵值對。
clear() 清空容器,即刪除容器中存儲的所有鍵值對。
swap() 交換 2 個 unordered_map 容器存儲的鍵值對,前提是必須保證這 2 個容器的類型完全相等。
bucket_count() 返回當前容器底層存儲鍵值對時,使用桶(一個線性鏈表代表一個桶)的數量。
max_bucket_count() 返回當前系統中,unordered_map 容器底層最多可以使用多少桶。
bucket_size(n) 返回第 n 個桶中存儲鍵值對的數量。
bucket(key) 返回以 key 為鍵的鍵值對所在桶的編號。
load_factor() 返回 unordered_map 容器中當前的負載因子。負載因子,指的是的當前容器中存儲鍵值對的數量(size())和使用桶數(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者設置當前 unordered_map 容器的負載因子。
rehash(n) 將當前容器底層使用桶的數量設置為 n。
reserve() 將存儲桶的數量(也就是 bucket_count() 方法的返回值)設置為至少容納 count 個元(不超過最大負載因子)所需的數量,並重新整理容器。
hash_function() 返回當前容器使用的哈希函數對象。

對於實現互換 2 個相同類型 unordered_map 容器的鍵值對,除了可以調用該容器模板類中提供的 swap() 成員方法外,STL 標准庫還提供了同名的 swap() 非成員函數。

2.3 無序容器的底層實現機制

C++ STL 標准庫中,不僅是 unordered_map 容器,所有無序容器的底層實現都采用的是哈希表存儲結構。更准確地說,是用“鏈地 址法”(又稱“開鏈法”)解決數據存儲位置發生沖突的哈希表,整個存儲結構如圖所示。

  • 當使用無序容器存儲鍵值對時,會先申請一整塊連續的存儲空間,但此空間並不用來直接存儲鍵值對,而是存儲各個鏈表的 頭指針,各鍵值對真正的存儲位置是各個鏈表的節點。
  • STL 標准庫通常選用 vector 容器存儲各個鏈表的頭指針。

當有新鍵值對存儲到無序容器中時,整個存儲過程分為好幾步:

  1. 將該鍵值對中鍵的值帶入設計好的哈希函數,會得到一個哈希值(一個整數,用 H 表示);
  2. 將 H 和無序容器擁有桶的數量 n 做整除運算(即 H % n),該結果即表示應將此鍵值對存儲到的桶的編號;
  3. 建立一個新節點存儲此鍵值對,同時將該節點鏈接到相應編號的桶上。

哈希表存儲結構有一個重要的屬性,稱為負載因子(load factor)。該屬性同樣適用於無序容器,用於衡量容器 存儲鍵值對的空/滿程序,即負載因子越大,意味着容器越滿,即各鏈表中掛載着越多的鍵值對,這無疑會降低容器查找目標鍵值對的效率;反之,負載因子越小,容器肯定越空,但並不一定各個鏈表中掛載的鍵值對就越少。

無序容器中,負載因子的計算方法為: 負載因子 = 容器存儲的總鍵值對 / 桶數

默認情況下,無序容器的最大負載因子為 1.0。如果操作無序容器過程中,使得最大復雜因子超過了默認值,則容器會自動增加桶數, 並重新進行哈希,以此來減小負載因子的值。需要注意的是,此過程會導致容器迭代器失效,但指向單個鍵值對的引用或者指針仍然有效。

無序容器管理哈希表的成員方法

成員方法 功能
bucket_count() 返回當前容器底層存儲鍵值對時,使用桶的數量。
max_bucket_count() 返回當前系統中,unordered_map 容器底層最多可以使用多少個桶。
bucket_size(n) 返回第 n 個桶中存儲鍵值對的數量。
bucket(key) 返回以 key 為鍵的鍵值對所在桶的編號。
load_factor() 返回 unordered_map 容器中當前的負載因子。
max_load_factor() 返回或者設置當前 unordered_map 容器的最大負載因子。
rehash(n) 嘗試重新調整桶的數量為等於或大於 n 的值。如果 n 大於當前容器使用的桶數,則該方法會是容器重新哈希, 該容器新的桶數將等於或大於 n。反之,如果 n 的值小於當前容器使用的桶數,則調用此方法可能沒有任何作用。
reserve(n) 將容器使用的桶數(bucket_count() 方法的返回值)設置為最適合存儲 n 個元素的桶數。
hash_function() 返回當前容器使用的哈希函數對象。

2.4 成員方法

成員方法 功能
begin() 返回指向容器中第一個鍵值對的正向迭代器。
end() 返回指向容器中最后一個鍵值對之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不過在其基礎上增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
cend() 和 end() 功能相同,只不過在其基礎上,增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
find(key) 查找以 key 為鍵的鍵值對,如果找到,則返回一個指向該鍵值對的正向迭代器;反之,則返回一個指向容器中最后 一個鍵值對之后位置的迭代器(如果 end() 方法返回的迭代器)。
equal_range(key) 返回一個 pair 對象,其包含 2 個迭代器,用於表明當前容器中鍵為 key 的鍵值對所在的范圍。

equal_range(key) 很少用,因為該容器中存儲的鍵值都不相等

2.5 獲取元素的 4 種方法

  1. [] 運算符,利用下標訪問普通數組中元素,如果沒有則添加
// 創建 umap 容器 
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"}, 
    {"Linux 教程","http://c.biancheng.net/linux/"} }; 

// 獲取 "Java 教程" 對應的值
string str = umap["Java 教程"];

// 添加
umap["C 教程"] = "http://c.biancheng.net/c/";
  1. at() 成員方法,查找失敗會報錯
//創建 umap 容器 
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"}, 
    {"Linux 教程","http://c.biancheng.net/linux/"} };

//獲取指定鍵對應的值
string str = umap.at("Python 教程");
  1. find() 成員方法。成功返回指向該鍵值對的迭代器,失敗返回 end() 方法一致的迭代器,指向最后一個鍵值對之后位置。
unordered_map<string, string> umap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"},
    {"Linux 教程","http://c.biancheng.net/linux/"} };

unordered_map<string, string>::iterator iter = umap.find("Python 教程");
unordered_map<string, string>::iterator iter2 = umap.find("GO 教程");   // 查找失敗
if (iter2 == umap.end()) 
    cout << "當前容器中沒有以\"GO 教程\"為鍵的鍵值對";
  1. 通過 begin()/end() 或者 cbegin()/cend() 遍歷

2.6 insert() 用法

insert() 方法可以向已建 unordered_map 容器中添加新的鍵值對。根據功能的不同,共有四種用法。

  1. 將 pair 類型鍵值對添加到容器中
// 以普通方式傳遞參數 
pair<iterator,bool> insert ( const value_type& val ); 
// 以右值引用的方式傳遞參數 
template <class P>
  pair<iterator,bool> insert ( P&& val );

返回 pair 類型值,內部包含一個 iterator 迭代器和 bool 變量,添加成功 bool 為 True,添加失敗 bool 為 False。

// 創建空 umap 容器 
unordered_map<string, string> umap; 
// 構建要添加的鍵值對 
std::pair<string, string>mypair("STL 教程", "http://c.biancheng.net/stl/"); 
// 創建接收 insert() 方法返回值的 pair 類型變量 
std::pair<unordered_map<string, string>::iterator, bool> ret; 
// 調用 insert() 方法的第一種語法格式
ret = umap.insert(mypair);

// 調用 insert() 方法的第二種語法格式
ret = umap.insert(std::make_pair("Python 教程","http://c.biancheng.net/python/"));
  1. 指定新鍵值對要添加到容器中的位置
// 以普通方式傳遞 val 參數 
iterator insert ( const_iterator hint, const value_type& val ); 
// 以右值引用方法傳遞 val 參數 
template <class P>
  iterator insert ( const_iterator hint, P&& val );

hint 參數為迭代器,用於指定新鍵值對要添加到容器中的位置;val 參數指的是要添加容器中的鍵值對;添加成功返回的迭代器指向新添加的鍵值對,失敗返回的迭代器指向容器中已有相同的鍵值對。注意最終存儲的位置實際上不取決於 hint 參數(hash)

// 創建空 umap 容器 
unordered_map<string, string> umap; 
// 構建要添加的鍵值對 
std::pair<string, string>mypair("STL 教程", "http://c.biancheng.net/stl/"); 
// 創建接收 insert() 方法返回值的迭代器類型變量 
unordered_map<string, string>::iterator iter; 
// 調用第一種語法格式
iter = umap.insert(umap.begin(), mypair);

// 調用第二種語法格式 
iter = umap.insert(umap.begin(),std::make_pair("Python 教程", "http://c.biancheng.net/python/"));
  1. 將某個 unordered_map 容器指定區域所有鍵值對復制到另一個 unordered_map 容器中
template <class InputIterator> 
  void insert ( InputIterator first, InputIterator last );

其中 first 和 last 都為迭代器,[first, last)表示復制其它 unordered_map 容器中鍵值對的區域。

// 創建並初始化 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程","http://c.biancheng.net/stl/"}, 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"} }; 
// 創建一個空的 unordered_map 容器 
unordered_map<string, string> otherumap; 

// 指定要拷貝 umap 容器中鍵值對的范圍 
unordered_map<string, string>::iterator first = ++umap.begin(); 
unordered_map<string, string>::iterator last = umap.end(); 
// 將指定 umap 容器中 [first,last) 區域內的鍵值對復制給 otherumap 容器
otherumap.insert(first, last);
  1. 一次想 unordered_map 容器添加多個鍵值對
void insert(initializer_list<value_type> il);

il 參數指可用於初始化列表的形式指定多個鍵值對元素

// 創建空的 umap 容器 
unordered_map<string, string> umap;
umap.insert({ {"STL 教程","http://c.biancheng.net/stl/"}, 
              {"Python 教程","http://c.biancheng.net/python/"},
              {"Java 教程","http://c.biancheng.net/java/"} });

2.7 emplace() 和 emplace_hint() 方法

template <class... Args> 
  pair<iterator, bool> emplace ( Args&&... args );

參數 args 表示可直接向該方法傳遞創建新鍵值對所需要的 2 個元素的值,其中第一個元素將作為鍵值對的鍵,另一個作為鍵值對的值。

  • 當 emplace() 成功添加新鍵值對時,返回的迭代器指向新添加的鍵值對,bool 值為 True;
  • 當 emplace() 添加新鍵值對失敗時,說明容器中本就包含一個鍵相等的鍵值對,此時返回的迭代器指向的就是容器中鍵相同的這個鍵值對,bool 值為 False。
// 創建 umap 容器 
unordered_map<string, string> umap; 
// 定義一個接受 emplace() 方法的 pair 類型變量 
pair<unordered_map<string, string>::iterator, bool> ret; 
// 調用 emplace() 方法 
ret = umap.emplace("STL 教程", "http://c.biancheng.net/stl/");

emplace_hint() 方法的語法格式如下:

template <class... Args> 
  iterator emplace_hint ( const_iterator position, Args&&... args );

emplace_hint 不同之處:

  • emplace_hint() 方法的返回值僅是一個迭代器,而不再是 pair 類型變量。當該方法將新鍵值對成功添加到容器中時,返回的迭代器指 向新添加的鍵值對;反之,如果添加失敗,該迭代器指向的是容器中和要添加鍵值對鍵相同的那個鍵值對。
  • emplace_hint() 方法還需要傳遞一個迭代器作為第一個參數,該迭代器表明將新鍵值對添加到容器中的位置。需要注意的是,新鍵值對添加到容器中的位置,並不是此迭代器說了算,最終仍取決於該鍵值對的鍵的值。該迭代器僅是建議。
// 創建 umap 容器 
unordered_map<string, string> umap; 
// 定義一個接受 emplace_hint() 方法的迭代器 
unordered_map<string,string>::iterator iter; 
// 調用 empalce_hint() 方法 
iter = umap.emplace_hint(umap.begin(),"STL 教程", "http://c.biancheng.net/stl/");

2.8 刪除元素 erase()/clear()

  • erase():刪除 unordered_map 容器中指定的鍵值對;
  • clear():刪除 unordered_map 容器中所有的鍵值對,即清空容器。

erase()

  1. 接受一個正向迭代器,並刪除該迭代器指向的鍵值對
// 創建 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
// 定義一個接收 erase() 方法的迭代器 
unordered_map<string,string>::iterator ret; 
// 刪除容器中第一個鍵值對
ret = umap.erase(umap.begin());

返回一個指向被刪除鍵值對之后位置的迭代器。
2. 根據鍵值對的鍵來刪除鍵值對

size_type erase ( const key_type& k );

k 表示目標鍵值對的鍵的值;返回一個整數,表示成功刪除的鍵值對的數量

// 創建 umap 容器 
unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
int delNum = umap.erase("Python 教程");     // delNum 為 1    
  1. 刪除指定范圍內的所有鍵值對
unordered_map<string, string> umap{ 
    {"STL 教程", "http://c.biancheng.net/stl/"}, 
    {"Python 教程", "http://c.biancheng.net/python/"},
    {"Java 教程", "http://c.biancheng.net/java/"} };
unordered_map<string, string>::iterator first = umap.begin(); 
unordered_map<string, string>::iterator last = --umap.end(); 
// 刪除[fist,last)范圍內的鍵值對
auto ret = umap.erase(first, last);   // 將僅剩最后一個鍵值對

clear()

一次性刪除 unordered_map 容器中所有鍵值對

  unordered_map<string, string> umap{ 
      {"STL 教程", "http://c.biancheng.net/stl/"}, 
      {"Python 教程", "http://c.biancheng.net/python/"},
      {"Java 教程", "http://c.biancheng.net/java/"} };
  umap.clear();     // umap 容器清空,umap.size() = 0

三、unordered_multimap 容器

和 unordered_map 容器一樣,unordered_multimap 容器也以鍵值對的形式存儲數據,且底層也采用哈希表結構存儲各個鍵值對。 兩者唯一的不同之處在於,unordered_multimap 容器可以存儲多個鍵相等的鍵值對。

unordered_multimap 容器存儲的所有鍵值對,鍵相等的鍵值對會被哈希到同一個桶中存儲。

unordered_multimap 定義在 <unordered_map> 頭文件中

#include <unordered_map>
using namespace std;

template < class Key,                         // 鍵(key)的類型
           class T,                           // 值(value)的類型 
          class Hash = hash<Key>,             // 底層存儲鍵值對時采用的哈希函數 
          class Pred = equal_to<Key>,         // 判斷各個鍵值對的鍵相等的規則 
          class Alloc = allocator< pair<const Key,T> >      // 指定分配器對象的類型
          > class unordered_multimap;
參數 含義
<key,T> 前 2 個參數分別用於確定鍵值對中鍵和值的類型,也就是存儲鍵值對的類型。
Hash = hash 用於指明容器在存儲各個鍵值對時要使用的哈希函數,默認使用 STL 標准庫提供的 hash 哈希函數。注 意,默認哈希函數只適用於基本數據類型(包括 string 類型),而不適用於自定義的結構體或者類。
Pred = equal_to unordered_multimap 容器可以存儲多個鍵相等的鍵值對,而判斷是否相等的規則,由此參數指定。默認情況下,使用 STL 標准庫中提供的 equal_to 規則,該規則僅支持可直接用 == 運算符做比較的數據類型。

當 unordered_multimap 容器中存儲鍵值對的鍵為自定義類型時,默認的哈希函數 hash 以及比較函數 equal_to 將不再適用,這種情況下,需要我們自定義適用的哈希函數和比較函數,並分別顯式傳遞給 Hash 參數和 Pred 參數。

3.1 創建 unordered_multimap 容器的方法

  1. 默認構造函數
std::unordered_multimap<std::string, std::string> myummap;
  1. 創建同時完成初始化
unordered_multimap<string, string>myummap{ 
    {"Python 教程","http://c.biancheng.net/python/"}, 
    {"Java 教程","http://c.biancheng.net/java/"},
    {"Linux 教程","http://c.biancheng.net/linux/"} };
  1. 拷貝/移動構造函數
unordered_multimap<string, string> myummap2(myummap);

// 返回臨時 unordered_multimap 容器的函數 
std::unordered_multimap <std::string, std::string > retUmmap() { 
    std::unordered_multimap<std::string, std::string>tempummap{ 
        {"Python 教程","http://c.biancheng.net/python/"}, 
        {"Java 教程","http://c.biancheng.net/java/"}, 
        {"Linux 教程","http://c.biancheng.net/linux/"} };
    return tempummap
}

// 創建並初始化 myummap 容器
std::unordered_multimap<std::string, std::string> myummap(retummap());
  1. 根據已有 unordered_map 容器部分區域新建 unordered_multimap 容器
// 傳入 2 個迭代器
std::unordered_multimap<std::string, std::string> myummap2(++myummap.begin(), myummap.end());

3.2 成員方法

成員方法 功能
begin() 返回指向容器中第一個鍵值對的正向迭代器。
end() 返回指向容器中最后一個鍵值對之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不過在其基礎上增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
cend() 和 end() 功能相同,只不過在其基礎上,增加了 const 屬性,即該方法返回的迭代器不能用於修改容器內存儲的鍵值對。
empty() 若容器為空,則返回 true;否則 false。
size() 返回當前容器中存有鍵值對的個數。
max_size() 返回容器所能容納鍵值對的最大個數,不同的操作系統,其返回值亦不相同。
find(key) 查找以 key 為鍵的鍵值對,如果找到,則返回一個指向該鍵值對的正向迭代器;反之,則返回一個指向容器中 最后一個鍵值對之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找以 key 鍵的鍵值對的個數。
equal_range(key) 返回一個 pair 對象,其包含 2 個迭代器,用於表明當前容器中鍵為 key 的鍵值對所在的范圍。
emplace() 向容器中添加新鍵值對,效率比 insert() 方法高。
emplace_hint() 向容器中添加新鍵值對,效率比 insert() 方法高。
insert() 向容器中添加新鍵值對。
erase() 刪除指定鍵值對。
clear() 清空容器,即刪除容器中存儲的所有鍵值對。
swap() 交換 2 個 unordered_multimap 容器存儲的鍵值對,前提是必須保證這 2 個容器的類型完全相等。
bucket_count() 返回當前容器底層存儲鍵值對時,使用桶(一個線性鏈表代表一個桶)的數量。
max_bucket_count() 返回當前系統中,unordered_multimap 容器底層最多可以使用多少桶。
bucket_size(n) 返回第 n 個桶中存儲鍵值對的數量。
bucket(key) 返回以 key 為鍵的鍵值對所在桶的編號。
load_factor() 返回 unordered_multimap 容器中當前的負載因子。負載因子,指的是的當前容器中存儲鍵值對的數量 (size())和使用桶數(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者設置當前 unordered_multimap 容器的負載因子。
rehash(n) 將當前容器底層使用桶的數量設置為 n。
reserve() 將存儲桶的數量(也就是 bucket_count() 方法的返回值)設置為至少容納 count 個元(不超過最大負載因子) 所需的數量,並重新整理容器。
hash_function() 返回當前容器使用的哈希函數對象。

四、unordered_set 容器

unordered_set 容器和 set 容器很像,唯一的區別就在於 set 容器會自行對存 儲的數據進行排序,而 unordered_set 容器不會。

unordered_set 容器具有以下幾個特性:

  1. 不再以鍵值對的形式存儲數據,而是直接存儲數據的值;
  2. 容器內部存儲的各個元素的值都互不相等,且不能被修改。
  3. 不會對內部存儲的數據進行排序;
#include <unordered_set> 
using namespace std;

// 類模板定義
template < class Key,                     // 容器中存儲元素的類型 
           class Hash = hash<Key>,        // 確定元素存儲位置所用的哈希函數 
           class Pred = equal_to<Key>,    // 判斷各個元素是否相等所用的函數 
           class Alloc = allocator<Key>   // 指定分配器對象的類型
           > class unordered_set;
參數 含義
Key 確定容器存儲元素的類型,如果讀者將 unordered_set 看做是存儲鍵和值相同的鍵值對的容器,則此參數則用於 確定各個鍵值對的鍵和值的類型,因為它們是完全相同的,因此一定是同一數據類型的數據。
Hash = hash 指定 unordered_set 容器底層存儲各個元素時,所使用的哈希函數。需要注意的是,默認哈希函數 hash 只適用於基本數據類型(包括 string 類型),而不適用於自定義的結構體或者類。
Pred = equal_to unordered_set 容器內部不能存儲相等的元素,而衡量 2 個元素是否相等的標准,取決於該參數指定的函數。 默 認情況下,使用 STL 標准庫中提供的 equal_to 規則,該規則僅支持可直接用 == 運算符做比較的數據類型。

4.1 創建 unordered_set 容器

  1. 默認構造函數
std::unordered_set<std::string> uset;
  1. 創建時初始化
std::unordered_set<std::string> uset{ "http://c.biancheng.net/c/", 
                                      "http://c.biancheng.net/java/",
                                      "http://c.biancheng.net/linux/" };
  1. 拷貝/移動構造函數
// 拷貝構造函數
std::unordered_set<std::string> uset2(uset);

// 移動構造函數
// 返回臨時 unordered_set 容器的函數 
std::unordered_set <std::string> retuset() { 
    std::unordered_set<std::string> tempuset{ "http://c.biancheng.net/c/", 
                                              "http://c.biancheng.net/java/",
                                              "http://c.biancheng.net/linux/" };
    return tempuset;
}

std::unordered_set<std::string> uset(retuset());
  1. 通過已有 unordered_set 部分區域新建
std::unordered_set<std::string> uset2(++uset.begin(), uset.end());

4.2 成員方法

成員方法 功能
begin() 返回指向容器中第一個元素的正向迭代器。
end() 返回指向容器中最后一個元素之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不過其返回的是 const 類型的正向迭代器。
cend() 和 end() 功能相同,只不過其返回的是 const 類型的正向迭代器。
empty() 若容器為空,則返回 true;否則 false。
size() 返回當前容器中存有元素的個數。
max_size() 返回容器所能容納元素的最大個數,不同的操作系統,其返回值亦不相同。
find(key) 查找以值為 key 的元素,如果找到,則返回一個指向該元素的正向迭代器;反之,則返回一個指向容器中最后 一個元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找值為 key 的元素的個數。
equal_range(key) 返回一個 pair 對象,其包含 2 個迭代器,用於表明當前容器中值為 key 的元素所在的范圍。
emplace() 向容器中添加新元素,效率比 insert() 方法高。
emplace_hint() 向容器中添加新元素,效率比 insert() 方法高。
insert() 向容器中添加新元素。
erase() 刪除指定元素。
clear() 清空容器,即刪除容器中存儲的所有元素。
swap() 交換 2 個 unordered_map 容器存儲的元素,前提是必須保證這 2 個容器的類型完全相等。
bucket_count() 返回當前容器底層存儲元素時,使用桶(一個線性鏈表代表一個桶)的數量。
max_bucket_count() 返回當前系統中,unordered_map 容器底層最多可以使用多少桶。
bucket_size(n) 返回第 n 個桶中存儲元素的數量。
bucket(key) 返回值為 key 的元素所在桶的編號。
load_factor() 返回 unordered_map 容器中當前的負載因子。負載因子,指的是的當前容器中存儲元素的數量(size())和使 用桶數(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者設置當前 unordered_map 容器的負載因子。
rehash(n) 將當前容器底層使用桶的數量設置為 n。
reserve() 將存儲桶的數量(也就是 bucket_count() 方法的返回值)設置為至少容納 count 個元(不超過最大負載因子) 所需的數量,並重新整理容器。
hash_function() 返回當前容器使用的哈希函數對象。

五、unordered_multiset 容器

  • unordered_multiset 不以鍵值對的形式存儲數據,而是直接存儲數據的值;
  • 該類型容器底層采用的也是哈希表存儲結構,它不會對內部存儲的數 據進行排序;
  • unordered_multiset 容器內部存儲的元素,其值不能被修改。
  • 和 unordered_set 容器不同的是,unordered_multiset 容器可以同時存儲多個值相同的元素,且這些元素會存儲到哈希表中同一個桶 (本質就是鏈表)上。
#include <unordered_set> 
using namespace std;

// unordered_multiset 容器類模板定義
template < class Key,                       // 容器中存儲元素的類型 
           class Hash = hash<Key>,          // 確定元素存儲位置所用的哈希函數 
           class Pred = equal_to<Key>,      // 判斷各個元素是否相等所用的函數 
           class Alloc = allocator<Key>     // 指定分配器對象的類型
           > class unordered_multiset;
參數 含義
Key 確定容器存儲元素的類型,如果讀者將 unordered_multiset 看做是存儲鍵和值相同的鍵值對的容器,則此參數 則用於確定各個鍵值對的鍵和值的類型,因為它們是完全相同的,因此一定是同一數據類型的數據。
Hash = hash 指定 unordered_multiset 容器底層存儲各個元素時所使用的哈希函數。需要注意的是,默認哈希函數 hash 只適用於基本數據類型(包括 string 類型),而不適用於自定義的結構體或者類。
Pred = equal_to 用於指定 unordered_multiset 容器判斷元素值相等的規則。默認情況下,使用 STL 標准庫中提供的equal_to 規則,該規則僅支持可直接用 == 運算符做比較的數據類型。

5.1 創建 unordered_multiset 容器

  1. 構造函數
std::unordered_multiset<std::string> umset;
  1. 創建容器同時初始化
std::unordered_multiset<std::string> umset{ "http://c.biancheng.net/c/", 
                                            "http://c.biancheng.net/java/",
                                            "http://c.biancheng.net/linux/" };
  1. 拷貝/移動構造函數
// 拷貝構造函數
std::unordered_multiset<std::string> umset2(umset);

// 移動構造函數
// 返回臨時 unordered_multiset 容器的函數 
std::unordered_multiset <std::string> retumset() { 
    std::unordered_multiset<std::string> tempumset{ 
        "http://c.biancheng.net/c/", 
        "http://c.biancheng.net/java/",
        "http://c.biancheng.net/linux/" };
    return tempumset;
}

// 調用移動構造函數
std::unordered_multiset<std::string> umset(retumset());
  1. 根據已有 unordered_multiset 容器部分區域新建
std::unordered_multiset<std::string> umset2(++umset.begin(), umset.end());

5.2 成員方法

成員方法 功能
begin() 返回指向容器中第一個元素的正向迭代器。
end() 返回指向容器中最后一個元素之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不過其返回的是 const 類型的正向迭代器。
cend() 和 end() 功能相同,只不過其返回的是 const 類型的正向迭代器。
empty() 若容器為空,則返回 true;否則 false。
size() 返回當前容器中存有元素的個數。
max_size() 返回容器所能容納元素的最大個數,不同的操作系統,其返回值亦不相同。
find(key) 查找以值為 key 的元素,如果找到,則返回一個指向該元素的正向迭代器;反之,則返回一個指向容器中最后 一個元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找值為 key 的元素的個數。
equal_range(key) 返回一個 pair 對象,其包含 2 個迭代器,用於表明當前容器中值為 key 的元素所在的范圍。
emplace() 向容器中添加新元素,效率比 insert() 方法高。
emplace_hint() 向容器中添加新元素,效率比 insert() 方法高。
insert() 向容器中添加新元素。
erase() 刪除指定元素。
clear() 清空容器,即刪除容器中存儲的所有元素。
swap() 交換 2 個 unordered_multimap 容器存儲的元素,前提是必須保證這 2 個容器的類型完全相等。
bucket_count() 返回當前容器底層存儲元素時,使用桶(一個線性鏈表代表一個桶)的數量。
max_bucket_count() 返回當前系統中,容器底層最多可以使用多少桶。
bucket_size(n) 返回第 n 個桶中存儲元素的數量。
bucket(key) 返回值為 key 的元素所在桶的編號。
load_factor() 返回容器當前的負載因子。所謂負載因子,指的是的當前容器中存儲元素的數量(size())和使用桶數 (bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者設置當前 unordered_map 容器的負載因子。
rehash(n) 將當前容器底層使用桶的數量設置為 n。
reserve() 將存儲桶的數量(也就是 bucket_count() 方法的返回值)設置為至少容納 count 個元(不超過最大負載因子) 所需的數量,並重新整理容器。
hash_function() 返回當前容器使用的哈希函數對象。


免責聲明!

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



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