一、什么是無序容器
無序容器是 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);
};
對於自定義的類型,實現自定義比較規則有兩種方法:
- 在自定義類中重載
==運算符,這會使在自定義類的std::equal_to<key>中使用==運算符合法。- 以函數對象類的方式,自定義一個適用於 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 為准)提供了以下幾種容器供我們選擇:
- 序列式容器:array、vector、deque、list 和 forward_list;
- 關聯式容器:map、multimap、set 和 multiset;
- 無序關聯式容器:unordered_map、unordered_multimap、unordered_set 和 unordered_multiset;
- 容器適配器: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
|
| 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 容器存儲各個鏈表的頭指針。
當有新鍵值對存儲到無序容器中時,整個存儲過程分為好幾步:
- 將該鍵值對中鍵的值帶入設計好的哈希函數,會得到一個哈希值(一個整數,用 H 表示);
- 將 H 和無序容器擁有桶的數量 n 做整除運算(即 H % n),該結果即表示應將此鍵值對存儲到的桶的編號;
- 建立一個新節點存儲此鍵值對,同時將該節點鏈接到相應編號的桶上。
哈希表存儲結構有一個重要的屬性,稱為負載因子(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 種方法
- [] 運算符,利用下標訪問普通數組中元素,如果沒有則添加
// 創建 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/";
- 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 教程");
- 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 教程\"為鍵的鍵值對";
- 通過 begin()/end() 或者 cbegin()/cend() 遍歷
2.6 insert() 用法
insert() 方法可以向已建 unordered_map 容器中添加新的鍵值對。根據功能的不同,共有四種用法。
- 將 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/"));
- 指定新鍵值對要添加到容器中的位置
// 以普通方式傳遞 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/"));
- 將某個 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);
- 一次想 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()
- 接受一個正向迭代器,並刪除該迭代器指向的鍵值對
// 創建 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
- 刪除指定范圍內的所有鍵值對
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
|
| Pred = equal_to
|
unordered_multimap 容器可以存儲多個鍵相等的鍵值對,而判斷是否相等的規則,由此參數指定。默認情況下,使用 STL 標准庫中提供的 equal_to
|
當 unordered_multimap 容器中存儲鍵值對的鍵為自定義類型時,默認的哈希函數 hash
以及比較函數 equal_to 將不再適用,這種情況下,需要我們自定義適用的哈希函數和比較函數,並分別顯式傳遞給 Hash 參數和 Pred 參數。
3.1 創建 unordered_multimap 容器的方法
- 默認構造函數
std::unordered_multimap<std::string, std::string> myummap;
- 創建同時完成初始化
unordered_multimap<string, string>myummap{
{"Python 教程","http://c.biancheng.net/python/"},
{"Java 教程","http://c.biancheng.net/java/"},
{"Linux 教程","http://c.biancheng.net/linux/"} };
- 拷貝/移動構造函數
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());
- 根據已有 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 容器具有以下幾個特性:
- 不再以鍵值對的形式存儲數據,而是直接存儲數據的值;
- 容器內部存儲的各個元素的值都互不相等,且不能被修改。
- 不會對內部存儲的數據進行排序;
#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
|
| Pred = equal_to
|
unordered_set 容器內部不能存儲相等的元素,而衡量 2 個元素是否相等的標准,取決於該參數指定的函數。 默 認情況下,使用 STL 標准庫中提供的 equal_to
|
4.1 創建 unordered_set 容器
- 默認構造函數
std::unordered_set<std::string> uset;
- 創建時初始化
std::unordered_set<std::string> uset{ "http://c.biancheng.net/c/",
"http://c.biancheng.net/java/",
"http://c.biancheng.net/linux/" };
- 拷貝/移動構造函數
// 拷貝構造函數
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());
- 通過已有 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
|
| Pred = equal_to
|
用於指定 unordered_multiset 容器判斷元素值相等的規則。默認情況下,使用 STL 標准庫中提供的equal_to
|
5.1 創建 unordered_multiset 容器
- 構造函數
std::unordered_multiset<std::string> umset;
- 創建容器同時初始化
std::unordered_multiset<std::string> umset{ "http://c.biancheng.net/c/",
"http://c.biancheng.net/java/",
"http://c.biancheng.net/linux/" };
- 拷貝/移動構造函數
// 拷貝構造函數
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());
- 根據已有 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() | 返回當前容器使用的哈希函數對象。 |
