科技改變生活 · 科技引領未來
繼 map、multimap、set、multiset 關聯式容器之后,從本節開始,再講解一類“特殊”的關聯式容器,它們常被稱為“無序容器”、“哈希容器”或者“無序關聯容器”。
和關聯式容器一樣,無序容器也使用鍵值對(pair 類型)的方式存儲數據。不過,本教程將二者分開進行講解,因為它們有本質上的不同:
C++ STL 底層采用哈希表實現無序容器時,會將所有數據存儲到一整塊連續的內存空間中,并且當數據存儲位置發生沖突時,解決方法選用的是“鏈地址法”(又稱“開鏈法”)。有關哈希表存儲結構,讀者可閱讀《哈希表(散列表)詳解》一文做詳細了解。
基于底層實現采用了不同的數據結構,因此和關聯式容器相比,無序容器具有以下 2 個特點:
和關聯式容器一樣,無序容器只是一類容器的統稱,其包含有 4 個具體容器,分別為 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。
表 1 對這 4 種無序容器的功能做了詳細的介紹。
可能讀者已經發現,以上 4 種無序容器的名稱,僅是在前面所學的 4 種關聯式容器名稱的基礎上,添加了 &34;unordered_&34;。如果讀者已經學完了 map、multimap、set 和 multiset 容器不難發現,以 map 和 unordered_map 為例,其實它們僅有一個區別,即 map 容器內存會對存儲的鍵值對進行排序,而 unordered_map 不會。
也就是說,C++ 11 標準的 STL 中,在已提供有 4 種關聯式容器的基礎上,又新增了各自的“unordered”版本(無序版本、哈希版本),提高了查找指定元素的效率。
有讀者可能會問,既然無序容器和之前所學的關聯式容器類似,那么在實際使用中應該選哪種容器呢?總的來說,實際場景中如果涉及大量遍歷容器的操作,建議首選關聯式容器;反之,如果更多的操作是通過鍵獲取對應的值,則應首選無序容器。
為了加深讀者對無序容器的認識,這里以 unordered_map 容器為例,舉個例子(不必深究該容器的具體用法):
include
include
include
using namespace std;
int main()
{
//創建并初始化一個 unordered_map 容器,其存儲的 類型的鍵值對
std::unordered_map my_uMap{
{&34;C語言教程&34;,&34;http://c.biancheng.net/c/&34;},
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;} };
//查找指定鍵對應的值,效率比關聯式容器高
string str = my_uMap.at(&34;C語言教程&34;);
cout << &34;str = &34; << str << endl;
//使用迭代器遍歷哈希容器,效率不如關聯式容器
for (auto iter = my_uMap.begin(); iter != my_uMap.end(); ++iter)
{
//pair 類型鍵值對分為 2 部分
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序執行結果為:
str = http://c.biancheng.net/c/
C語言教程 http://c.biancheng.net/c/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
C++ STL 標準庫中提供有 4 種無序關聯式容器,本節先講解 unordered_map 容器。
unordered_map 容器,直譯過來就是&34;無序 map 容器&34;的意思。所謂“無序”,指的是 unordered_map 容器不會像 map 容器那樣對存儲的數據進行排序。換句話說,unordered_map 容器和 map 容器僅有一點不同,即 map 容器中存儲的數據是有序的,而 unordered_map 容器中是無序的。
對于已經學過 map 容器的讀者,可以將 unordered_map 容器等價為無序的 map 容器。
具體來講,unordered_map 容器和 map 容器一樣,以鍵值對(pair類型)的形式存儲數據,存儲的各個鍵值對的鍵互不相同且不允許被修改。但由于 unordered_map 容器底層采用的是哈希表存儲結構,該結構本身不具有對數據的排序功能,所以此容器內部不會自行對存儲的鍵值對進行排序。
值得一提的是,unordered_map 容器在
include using namespace std;
注意,第二行代碼不是必需的,但如果不用,則后續程序中在使用此容器時,需手動注明 std 命名空間(強烈建議初學者使用)。
unordered_map 容器模板的定義如下所示:
template < class Key, //鍵值對中鍵的類型
class T, //鍵值對中值的類型
class Hash = hash, //容器內部存儲鍵值對所用的哈希函數
class Pred = equal_to, //判斷各個鍵值對鍵相同的規則
class Alloc = allocator< pairnst Key,T> > // 指定分配器對象的類型
> class unordered_map;
以上 5 個參數中,必須顯式給前 2 個參數傳值,并且除特殊情況外,最多只需要使用前 4 個參數,各自的含義和功能如表 1 所示。
表 1 unordered_map 容器模板類的常用參數
總的來說,當無序容器中存儲鍵值對的鍵為自定義類型時,默認的哈希函數 hash 以及比較函數 equal_to 將不再適用,只能自己設計適用該類型的哈希函數和比較函數,并顯式傳遞給 Hash 參數和 Pred 參數。至于如何實現自定義,后續章節會做詳細講解。
常見的創建 unordered_map 容器的方法有以下幾種。
1) 通過調用 unordered_map 模板類的默認構造函數,可以創建空的 unordered_map 容器。比如:
std::unordered_map umap;
由此,就創建好了一個可存儲
2) 當然,在創建 unordered_map 容器的同時,可以完成初始化操作。比如:
std::unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
通過此方法創建的 umap 容器中,就包含有 3 個鍵值對元素。
3) 另外,還可以調用 unordered_map 模板中提供的復制(拷貝)構造函數,將現有 unordered_map 容器中存儲的鍵值對,復制給新建 unordered_map 容器。
例如,在第二種方式創建好 umap 容器的基礎上,再創建并初始化一個 umap2 容器:
std::unordered_map umap2(umap);
由此,umap2 容器中就包含有 umap 容器中所有的鍵值對。
除此之外,C++ 11 標準中還向 unordered_map 模板類增加了移動構造函數,即以右值引用的方式將臨時 unordered_map 容器中存儲的所有鍵值對,全部復制給新建容器。例如:
//返回臨時 unordered_map 容器的函數
std::unordered_map retUmap(){
std::unordered_maptempUmap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
return tempUmap;}
//調用移動構造函數,創建 umap2 容器
std::unordered_map umap2(retUmap());
注意,無論是調用復制構造函數還是拷貝構造函數,必須保證 2 個容器的類型完全相同。
4) 當然,如果不想全部拷貝,可以使用 unordered_map 類模板提供的迭代器,在現有 unordered_map 容器中選擇部分區域內的鍵值對,為新建 unordered_map 容器初始化。例如:
//傳入 2 個迭代器,
std::unordered_map
umap2(++umap.begin(),umap.end());
通過此方式創建的 umap2 容器,其內部就包含 umap 容器中除第 1 個鍵值對外的所有其它鍵值對。
unordered_map 既可以看做是關聯式容器,更屬于自成一脈的無序容器。因此在該容器模板類中,既包含一些在學習關聯式容器時常見的成員方法,還有一些屬于無序容器特有的成員方法。
表 2 列出了 unordered_map 類模板提供的所有常用的成員方法以及各自的功能。
注意,對于實現互換 2 個相同類型 unordered_map 容器的鍵值對,除了可以調用該容器模板類中提供的 swap() 成員方法外,STL 標準庫還提供了同名的 swap() 非成員函數。
下面的樣例演示了表 2 中部分成員方法的用法:
include
include
include
using namespace std;
int main()
{
//創建空 umap 容器
unordered_map umap;
//向 umap 容器添加新鍵值對
umap.emplace(&34;Python教程&34;, &34;http://c.biancheng.net/python/&34;);
umap.emplace(&34;Java教程&34;, &34;http://c.biancheng.net/java/&34;);
umap.emplace(&34;Linux教程&34;, &34;http://c.biancheng.net/linux/&34;);
//輸出 umap 存儲鍵值對的數量
cout << &34;umap size = &34; << umap.size() << endl;
//使用迭代器輸出 umap 容器存儲的所有鍵值對
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序執行結果為:
umap size = 3
Python教程 http://c.biancheng.net/python/
Linux教程 http://c.biancheng.net/linux/
Java教程 http://c.biancheng.net/java/
在了解哈希表存儲結構的基礎上,本節將具體分析 C++ STL 無序容器(哈希容器)底層的實現原理。
C++ STL 標準庫中,不僅是 unordered_map 容器,所有無序容器的底層實現都采用的是哈希表存儲結構。更準確地說,是用“鏈地址法”(又稱“開鏈法”)解決數據存儲位置發生沖突的哈希表,整個存儲結構如圖 1 所示。
其中,Pi 表示存儲的各個鍵值對。
可以看到,當使用無序容器存儲鍵值對時,會先申請一整塊連續的存儲空間,但此空間并不用來直接存儲鍵值對,而是存儲各個鏈表的頭指針,各鍵值對真正的存儲位置是各個鏈表的節點。
注意,STL 標準庫通常選用 vector 容器存儲各個鏈表的頭指針。
不僅如此,在 C++ STL 標準庫中,將圖 1 中的各個鏈表稱為桶(bucket),每個桶都有自己的編號(從 0 開始)。當有新鍵值對存儲到無序容器中時,整個存儲過程分為如下幾步:
另外值得一提的是,哈希表存儲結構還有一個重要的屬性,稱為負載因子(load factor)。該屬性同樣適用于無序容器,用于衡量容器存儲鍵值對的空/滿程序,即負載因子越大,意味著容器越滿,即各鏈表中掛載著越多的鍵值對,這無疑會降低容器查找目標鍵值對的效率;反之,負載因子越小,容器肯定越空,但并不一定各個鏈表中掛載的鍵值對就越少。
舉個例子,如果設計的哈希函數不合理,使得各個鍵值對的鍵帶入該函數得到的哈希值始終相同(所有鍵值對始終存儲在同一鏈表上)。這種情況下,即便增加桶數是的負載因子減小,該容器的查找效率依舊很差。
無序容器中,負載因子的計算方法為:
負載因子 = 容器存儲的總鍵值對 / 桶數
默認情況下,無序容器的最大負載因子為 1.0。如果操作無序容器過程中,使得最大復雜因子超過了默認值,則容器會自動增加桶數,并重新進行哈希,以此來減小負載因子的值。需要注意的是,此過程會導致容器迭代器失效,但指向單個鍵值對的引用或者指針仍然有效。
這也就解釋了,為什么我們在操作無序容器過程中,鍵值對的存儲順序有時會“莫名”的發生變動。
C++ STL 標準庫為了方便用戶更好地管控無序容器底層使用的哈希表存儲結構,各個無序容器的模板類中都提供表 2 所示的成員方法。
表 2 無序容器管理哈希表的成員方法
下面的程序以學過的 unordered_map 容器為例,演示了表 2 中部分成員方法的用法:
include
include
include
using namespace std;
int main()
{
//創建空 umap 容器
unordered_map umap;
cout << &34;umap 初始桶數: &34; << umap.bucket_count() << endl;
cout << &34;umap 初始負載因子: &34; << umap.load_factor() << endl;
cout << &34;umap 最大負載因子: &34; << umap.max_load_factor() << endl;
//設置 umap 使用最適合存儲 9 個鍵值對的桶數
umap.reserve(9);
cout << &34;*********************&34; << endl;
cout << &34;umap 新桶數: &34; << umap.bucket_count() << endl;
cout << &34;umap 新負載因子: &34; << umap.load_factor() << endl;
//向 umap 容器添加 3 個鍵值對
umap[&34;Python教程&34;] = &34;http://c.biancheng.net/python/&34;;
umap[&34;Java教程&34;] = &34;http://c.biancheng.net/java/&34;;
umap[&34;Linux教程&34;] = &34;http://c.biancheng.net/linux/&34;;
//調用 bucket() 獲取指定鍵值對位于桶的編號
cout << &34;以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:&34; << umap.bucket(&34;Python教程&34;) << endl;
//自行計算某鍵值對位于哪個桶
auto fn = umap.hash_function();
cout << &34;計算以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:&34; << fn(&34;Python教程&34;) % (umap.bucket_count()) << endl;
return 0;
}
程序執行結果為:
umap 初始桶數: 8
umap 初始負載因子: 0
umap 最大負載因子: 1
*********************
umap 新桶數: 16
umap 新負載因子: 0
以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:9
計算以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:9
從輸出結果可以看出,對于空的 umap 容器,初始狀態下會分配 8 個桶,并且默認最大負載因子為 1.0,但由于其為存儲任何鍵值對,因此負載因子值為 0。
與此同時,程序中調用 reverse() 成員方法,是 umap 容器的桶數改為了 16,其最適合存儲 9 個鍵值對。從側面可以看出,一旦負載因子大于 1.0(9/8 > 1.0),則容器所使用的桶數就會翻倍式(8、16、32、...)的增加。
程序最后還演示了如何手動計算出指定鍵值對存儲的桶的編號,其計算結果和使用 bucket() 成員方法得到的結果是一致的。
C++ STL 標準庫中,unordered_map 容器迭代器的類型為前向迭代器(又稱正向迭代器)。這意味著,假設 p 是一個前向迭代器,則其只能進行 *p、p++、++p 操作,且 2 個前向迭代器之間只能用 == 和 != 運算符做比較。
在 unordered_map 容器模板中,提供了表 1 所示的成員方法,可用來獲取指向指定位置的前向迭代器。
表 1 C++ unordered_map迭代器相關成員方法
值得一提的是,equal_range(key) 很少用于 unordered_map 容器,因為該容器中存儲的都是鍵不相等的鍵值對,即便調用該成員方法,得到的 2 個迭代器所表示的范圍中,最多只包含 1 個鍵值對。事實上,該成員方法更適用于 unordered_multimap 容器(該容器后續章節會做詳細講解)。
下面的程序演示了表 1 中部分成員方法的用法。
include
include
include
using namespace std;
int main()
{
//創建 umap 容器
unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
cout << &34;umap 存儲的鍵值對包括:&34; << endl;
//遍歷輸出 umap 容器中所有的鍵值對
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << &34;<&34; << iter->first << &34;, &34; << iter->second << &34;>&34; << endl;
}
//獲取指向指定鍵值對的前向迭代器
unordered_map::iterator iter = umap.find(&34;Java教程&34;);
cout <<&34;umap.find(&34;Java教程&34;) = &34; << &34;<&34; << iter->first << &34;, &34; << iter->second << &34;>&34; << endl;
return 0;
}
程序執行結果為:
umap 存儲的鍵值對包括:
umap.find(&34;Java教程&34;) =
需要注意的是,在操作 unordered_map 容器過程(尤其是向容器中添加新鍵值對)中,一旦當前容器的負載因子超過最大負載因子(默認值為 1.0),該容器就會適當增加桶的數量(通常是翻一倍),并自動執行 rehash() 成員方法,重新調整各個鍵值對的存儲位置(此過程又稱“重哈希”),此過程很可能導致之前創建的迭代器失效。
所謂迭代器失效,針對的是那些用于表示容器內某個范圍的迭代器,由于重哈希會重新調整每個鍵值對的存儲位置,所以容器重哈希之后,之前表示特定范圍的迭代器很可能無法再正確表示該范圍。但是,重哈希并不會影響那些指向單個鍵值對元素的迭代器。
舉個例子:
include
include
using namespace std;
int main()
{
//創建 umap 容器
unordered_map umap;
//向 umap 容器添加 50 個鍵值對
for (int i = 1; i <= 50; i++) {
umap.emplace(i, i);
}
//獲取鍵為 49 的鍵值對所在的范圍
auto pair = umap.equal_range(49);
//輸出 pair 范圍內的每個鍵值對的鍵的值
for (auto iter = pair.first; iter != pair.second; ++iter) {
cout << iter->first <<&34; &34;;
}
cout << endl;
//手動調整最大負載因子數
umap.max_load_factor(3.0);
//手動調用 rehash() 函數重哈希
umap.rehash(10);
//重哈希之后,pair 的范圍可能會發生變化
for (auto iter = pair.first; iter != pair.second; ++iter) {
cout << iter->first << &34; &34;;
}
return 0;
}
程序執行結果為:
49
49 17
觀察輸出結果不難發現,之前用于表示鍵為 49 的鍵值對所在范圍的 2 個迭代器,重哈希之后表示的范圍發生了改變。
經測試,用于遍歷整個容器的 begin()/end() 和 cbegin()/cend() 迭代器對,重哈希只會影響遍歷容器內鍵值對的順序,整個遍歷的操作仍然可以順利完成。
通過前面的學習我們知道,unordered_map 容器以鍵值對的方式存儲數據。為了方便用戶快速地從該類型容器提取出目標元素(也就是某個鍵值對的值),unordered_map 容器類模板中提供了以下幾種方法。
1) unordered_map 容器類模板中,實現了對 [ ] 運算符的重載,使得我們可以像“利用下標訪問普通數組中元素”那樣,通過目標鍵值對的鍵獲取到該鍵對應的值。
舉個例子:
include
include
include
using namespace std;
int main()
{
//創建 umap 容器
unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
//獲取 &34;Java教程&34; 對應的值
string str = umap[&34;Java教程&34;];
cout << str << endl;
return 0;
}
程序輸出結果為:
http://c.biancheng.net/java/
需要注意的是,如果當前容器中并沒有存儲以 [ ] 運算符內指定的元素作為鍵的鍵值對,則此時 [ ] 運算符的功能將轉變為:向當前容器中添加以目標元素為鍵的鍵值對。舉個例子:
include
include
include
using namespace std;
int main()
{
//創建空 umap 容器
unordered_map umap;
//[] 運算符在 = 右側
string str = umap[&34;STL教程&34;];
//[] 運算符在 = 左側
umap[&34;C教程&34;] = &34;http://c.biancheng.net/c/&34;;
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序執行結果為:
C教程 http://c.biancheng.net/c/
STL教程
可以看到,當使用 [ ] 運算符向 unordered_map 容器中添加鍵值對時,分為 2 種情況:
2) unordered_map 類模板中,還提供有 at() 成員方法,和使用 [ ] 運算符一樣,at() 成員方法也需要根據指定的鍵,才能從容器中找到該鍵對應的值;不同之處在于,如果在當前容器中查找失敗,該方法不會向容器中添加新的鍵值對,而是直接拋出out_of_range異常。
舉個例子:
include
include
include
using namespace std;
int main(){
//創建 umap 容器
unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
//獲取指定鍵對應的值
string str = umap.at(&34;Python教程&34;);
cout << str << endl;
//執行此語句會拋出 out_of_range 異常
//cout << umap.at(&34;GO教程&34;);
return 0;}
程序執行結果為:
http://c.biancheng.net/python/
此程序中,第 13 行代碼用于獲取 umap 容器中鍵為“Python教程”對應的值,由于 umap 容器確實有符合條件的鍵值對,因此可以成功執行;而第 17 行代碼,由于當前 umap 容器沒有存儲以“Go教程”為鍵的鍵值對,因此執行此語句會拋出 out_of_range 異常。
3) [ ] 運算符和 at() 成員方法基本能滿足大多數場景的需要。除此之外,還可以借助 unordered_map 模板中提供的 find() 成員方法。
和前面方法不同的是,通過 find() 方法得到的是一個正向迭代器,該迭代器的指向分以下 2 種情況:
舉個例子:
include
include
include
using namespace std;
int main(){
//創建 umap 容器
unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
//查找成功
unordered_map::iterator iter = umap.find(&34;Python教程&34;);
cout << iter->first << &34; &34; << iter->second << endl;
//查找失敗
unordered_map::iterator iter2 = umap.find(&34;GO教程&34;);
if (iter2 == umap.end()) {
cout << &34;當前容器中沒有以&34;GO教程&34;為鍵的鍵值對&34;;
}
return 0;}
程序執行結果為:
Python教程 http://c.biancheng.net/python/
當前容器中沒有以&34;GO教程&34;為鍵的鍵值對
4) 除了 find() 成員方法之外,甚至可以借助 begin()/end() 或者 cbegin()/cend(),通過遍歷整個容器中的鍵值對來找到目標鍵值對。
舉個例子:
include
include
include
using namespace std;
int main()
{
//創建 umap 容器
unordered_map umap{
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;},
{&34;Linux教程&34;,&34;http://c.biancheng.net/linux/&34;} };
//遍歷整個容器中存儲的鍵值對
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
//判斷當前的鍵值對是否就是要找的
if (!iter->first.compare(&34;Java教程&34;)) {
cout << iter->second << endl;
break;
}
}
return 0;
}
程序執行結果為:
http://c.biancheng.net/java/
以上 4 種方法中,前 2 種方法基本能滿足多數場景的需要,建議初學者首選 at() 成員方法!
為了方便用戶向已建 unordered_map 容器中添加新的鍵值對,該容器模板中提供了 insert() 方法,本節就對此方法的用法做詳細的講解。
unordered_map 模板類中,提供了多種語法格式的 insert() 方法,根據功能的不同,可劃分為以下幾種用法。
1) insert() 方法可以將 pair 類型的鍵值對元素添加到 unordered_map 容器中,其語法格式有 2 種:
//以普通方式傳遞參數
pair
//以右值引用的方式傳遞參數
template
pair
為了方便用戶向已建 unordered_map 容器中添加新的鍵值對,該容器模板中提供了 insert() 方法,本節就對此方法的用法做詳細的講解。
unordered_map 模板類中,提供了多種語法格式的 insert() 方法,根據功能的不同,可劃分為以下幾種用法。
1) insert() 方法可以將 pair 類型的鍵值對元素添加到 unordered_map 容器中,其語法格式有 2 種:
//以普通方式傳遞參數
pair
//以右值引用的方式傳遞參數
template
pair
以上 2 種格式中,參數 val 表示要添加到容器中的目標鍵值對元素;該方法的返回值為 pair類型值,內部包含一個 iterator 迭代器和 bool 變量:
include
include
include
using namespace std;
int main()
{
//創建空 umap 容器
unordered_map umap;
//構建要添加的鍵值對
std::pairmypair(&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//創建接收 insert() 方法返回值的pair類型變量
std::pair::iterator, bool> ret;
//調用 insert() 方法的第一種語法格式
ret = umap.insert(mypair);
cout << &34;bool = &34; << ret.second << endl;
cout << &34;iter -> &34; << ret.first->first <<&34; &34; << ret.first->second << endl;
//調用 insert() 方法的第二種語法格式
ret = umap.insert(std::make_pair(&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;));
cout << &34;bool = &34; << ret.second << endl;
cout << &34;iter -> &34; << ret.first->first << &34; &34; << ret.first->second << endl;
return 0;
}
程序執行結果為:
bool = 1
iter -> STL教程 http://c.biancheng.net/stl/
bool = 1
iter -> Python教程 http://c.biancheng.net/python/
從輸出結果很容易看出,兩次添加鍵值對的操作,insert() 方法返回值中的 bool 變量都為 1,表示添加成功,此時返回的迭代器指向的是添加成功的鍵值對。
2) 除此之外,insert() 方法還可以指定新鍵值對要添加到容器中的位置,其語法格式如下:
//以普通方式傳遞 val 參數
iterator insert ( const_iterator hint, const value_type& val );
//以右值引用方法傳遞 val 參數
template
iterator insert ( const_iterator hint, P&& val );
以上 2 種語法格式中,hint 參數為迭代器,用于指定新鍵值對要添加到容器中的位置;val 參數指的是要添加容器中的鍵值對;方法的返回值為迭代器:
注意,以上 2 種語法格式中,雖然通過 hint 參數指定了新鍵值對添加到容器中的位置,但該鍵值對真正存儲的位置,并不是 hint 參數說了算,最終的存儲位置仍取決于該鍵值對的鍵的值。
include
include
include
using namespace std;
int main()
{
//創建空 umap 容器
unordered_map umap;
//構建要添加的鍵值對
std::pairmypair(&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//創建接收 insert() 方法返回值的迭代器類型變量
unordered_map::iterator iter;
//調用第一種語法格式
iter = umap.insert(umap.begin(), mypair);
cout << &34;iter -> &34; << iter->first <<&34; &34; << iter->second << endl;
//調用第二種語法格式
iter = umap.insert(umap.begin(),std::make_pair(&34;Python教程&34;, &34;http://c.biancheng.net/python/&34;));
cout << &34;iter -> &34; << iter->first << &34; &34; << iter->second << endl;
return 0;
}
程序輸出結果為:
iter -> STL教程 http://c.biancheng.net/stl/
iter -> Python教程 http://c.biancheng.net/python/
3) insert() 方法還支持將某一個 unordered_map 容器中指定區域內的所有鍵值對,復制到另一個 unordered_map 容器中,其語法格式如下:
template
void insert ( InputIterator first, InputIterator last );
其中 first 和 last 都為迭代器,[first, last)表示復制其它 unordered_map 容器中鍵值對的區域。
include
include
include
using namespace std;
int main()
{
//創建并初始化 umap 容器
unordered_map umap{ {&34;STL教程&34;,&34;http://c.biancheng.net/stl/&34;},
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;} };
//創建一個空的 unordered_map 容器
unordered_map otherumap;
//指定要拷貝 umap 容器中鍵值對的范圍
unordered_map::iterator first = ++umap.begin();
unordered_map::iterator last = umap.end();
//將指定 umap 容器中 [first,last) 區域內的鍵值對復制給 otherumap 容器
otherumap.insert(first, last);
//遍歷 otherumap 容器中存儲的鍵值對
for (auto iter = otherumap.begin(); iter != otherumap.end(); ++iter){
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序輸出結果為:
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
4) 除了以上 3 種方式,insert() 方法還支持一次向 unordered_map 容器添加多個鍵值對,其語法格式如下:
void insert ( initializer_list
其中,il 參數指的是可以用初始化列表的形式指定多個鍵值對元素。
include
include
include
using namespace std;
int main()
{
//創建空的 umap 容器
unordered_map umap;
//向 umap 容器同時添加多個鍵值對
umap.insert({ {&34;STL教程&34;,&34;http://c.biancheng.net/stl/&34;},
{&34;Python教程&34;,&34;http://c.biancheng.net/python/&34;},
{&34;Java教程&34;,&34;http://c.biancheng.net/java/&34;} });
//遍歷輸出 umap 容器中存儲的鍵值對
for (auto iter = umap.begin(); iter != umap.end(); ++iter){
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序輸出結果為:
STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
和前面學的 map、set 等容器一樣,C++ 11 標準也為 unordered_map 容器新增了 emplace() 和 emplace_hint() 成員方法,本節將對它們的用法做詳細的介紹。
我們知道,實現向已有 unordered_map 容器中添加新鍵值對,可以通過調用 insert() 方法,但其實還有更好的方法,即使用 emplace() 或者 emplace_hint() 方法,它們完成“向容器中添加新鍵值對”的效率,要比 insert() 方法高。
emplace() 方法的用法很簡單,其語法格式如下:
template
pair
其中,參數 args 表示可直接向該方法傳遞創建新鍵值對所需要的 2 個元素的值,其中第一個元素將作為鍵值對的鍵,另一個作為鍵值對的值。也就是說,該方法無需我們手動創建鍵值對,其內部會自行完成此工作。
另外需要注意的是,該方法的返回值為 pair 類型值,其包含一個迭代器和一個 bool 類型值:
include
include
include
using namespace std;
int main()
{
//創建 umap 容器
unordered_map umap;
//定義一個接受 emplace() 方法的 pair 類型變量
pair::iterator, bool> ret;
//調用 emplace() 方法
ret = umap.emplace(&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//輸出 ret 中包含的 2 個元素的值
cout << &34;bool =&34; << ret.second << endl;
cout << &34;iter ->&34; << ret.first->first << &34; &34; << ret.first->second << endl;
return 0;
}
程序執行結果為:
bool =1
iter ->STL教程 http://c.biancheng.net/stl/
通過執行結果中 bool 變量的值為 1 可以得知,emplace() 方法成功將新鍵值對添加到了 umap 容器中。
emplace_hint() 方法的語法格式如下:
template
iterator emplace_hint ( const_iterator position, Args&&... args );
和 emplace() 方法相同,emplace_hint() 方法內部會自行構造新鍵值對,因此我們只需向其傳遞構建該鍵值對所需的 2 個元素(第一個作為鍵,另一個作為值)即可。不同之處在于:
可以這樣理解,emplace_hint() 方法中傳入的迭代器,僅是給 unordered_map 容器提供一個建議,并不一定會被容器采納。
舉個例子:
include
include
include
using namespace std;
int main(){
//創建 umap 容器
unordered_map umap;
//定義一個接受 emplace_hint() 方法的迭代器
unordered_map::iterator iter;
//調用 empalce_hint() 方法
iter = umap.emplace_hint(umap.begin(),&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//輸出 emplace_hint() 返回迭代器 iter 指向的鍵值對的內容
cout << &34;iter ->&34; << iter->first << &34; &34; << iter->second << endl;
return 0;
}
程序執行結果為:
iter ->STL教程 http://c.biancheng.net/stl/
高悅明