C++ STL無序容器(哈希容器)是什么?
繼 map、multimap、set、multiset 關(guān)聯(lián)式容器之后,從本節(jié)開始,再講解一類“特殊”的關(guān)聯(lián)式容器,它們常被稱為“無序容器”、“哈希容器”或者“無序關(guān)聯(lián)容器”。
和關(guān)聯(lián)式容器一樣,無序容器也使用鍵值對(pair 類型)的方式存儲數(shù)據(jù)。不過,本教程將二者分開進行講解,因為它們有本質(zhì)上的不同:
- 關(guān)聯(lián)式容器的底層實現(xiàn)采用的樹存儲結(jié)構(gòu),更確切的說是紅黑樹結(jié)構(gòu);
- 無序容器的底層實現(xiàn)采用的是哈希表的存儲結(jié)構(gòu)。
C++ STL 底層采用哈希表實現(xiàn)無序容器時,會將所有數(shù)據(jù)存儲到一整塊連續(xù)的內(nèi)存空間中,并且當數(shù)據(jù)存儲位置發(fā)生沖突時,解決方法選用的是“鏈地址法”(又稱“開鏈法”)。有關(guān)哈希表存儲結(jié)構(gòu),讀者可閱讀《哈希表(散列表)詳解》一文做詳細了解。
基于底層實現(xiàn)采用了不同的數(shù)據(jù)結(jié)構(gòu),因此和關(guān)聯(lián)式容器相比,無序容器具有以下 2 個特點:
- 無序容器內(nèi)部存儲的鍵值對是無序的,各鍵值對的存儲位置取決于該鍵值對中的鍵,
- 和關(guān)聯(lián)式容器相比,無序容器擅長通過指定鍵查找對應(yīng)的值(平均時間復(fù)雜度為 O(1));但對于使用迭代器遍歷容器中存儲的元素,無序容器的執(zhí)行效率則不如關(guān)聯(lián)式容器。
C++ STL無序容器種類
和關(guān)聯(lián)式容器一樣,無序容器只是一類容器的統(tǒng)稱,其包含有 4 個具體容器,分別為 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。
表 1 對這 4 種無序容器的功能做了詳細的介紹。

可能讀者已經(jīng)發(fā)現(xiàn),以上 4 種無序容器的名稱,僅是在前面所學的 4 種關(guān)聯(lián)式容器名稱的基礎(chǔ)上,添加了 &34;unordered_&34;。如果讀者已經(jīng)學完了 map、multimap、set 和 multiset 容器不難發(fā)現(xiàn),以 map 和 unordered_map 為例,其實它們僅有一個區(qū)別,即 map 容器內(nèi)存會對存儲的鍵值對進行排序,而 unordered_map 不會。
也就是說,C++ 11 標準的 STL 中,在已提供有 4 種關(guān)聯(lián)式容器的基礎(chǔ)上,又新增了各自的“unordered”版本(無序版本、哈希版本),提高了查找指定元素的效率。
有讀者可能會問,既然無序容器和之前所學的關(guān)聯(lián)式容器類似,那么在實際使用中應(yīng)該選哪種容器呢?總的來說,實際場景中如果涉及大量遍歷容器的操作,建議首選關(guān)聯(lián)式容器;反之,如果更多的操作是通過鍵獲取對應(yīng)的值,則應(yīng)首選無序容器。
為了加深讀者對無序容器的認識,這里以 unordered_map 容器為例,舉個例子(不必深究該容器的具體用法):
include
include
include
using namespace std;
int main()
{
//創(chuàng)建并初始化一個 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;} };
//查找指定鍵對應(yīng)的值,效率比關(guān)聯(lián)式容器高
string str = my_uMap.at(&34;C語言教程&34;);
cout << &34;str = &34; << str << endl;
//使用迭代器遍歷哈希容器,效率不如關(guān)聯(lián)式容器
for (auto iter = my_uMap.begin(); iter != my_uMap.end(); ++iter)
{
//pair 類型鍵值對分為 2 部分
cout << iter->first << &34; &34; << iter->second << endl;
}
return 0;
}
程序執(zhí)行結(jié)果為:
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 unordered_map容器用法詳解
C++ STL 標準庫中提供有 4 種無序關(guān)聯(lián)式容器,本節(jié)先講解 unordered_map 容器。
unordered_map 容器,直譯過來就是&34;無序 map 容器&34;的意思。所謂“無序”,指的是 unordered_map 容器不會像 map 容器那樣對存儲的數(shù)據(jù)進行排序。換句話說,unordered_map 容器和 map 容器僅有一點不同,即 map 容器中存儲的數(shù)據(jù)是有序的,而 unordered_map 容器中是無序的。
對于已經(jīng)學過 map 容器的讀者,可以將 unordered_map 容器等價為無序的 map 容器。
具體來講,unordered_map 容器和 map 容器一樣,以鍵值對(pair類型)的形式存儲數(shù)據(jù),存儲的各個鍵值對的鍵互不相同且不允許被修改。但由于 unordered_map 容器底層采用的是哈希表存儲結(jié)構(gòu),該結(jié)構(gòu)本身不具有對數(shù)據(jù)的排序功能,所以此容器內(nèi)部不會自行對存儲的鍵值對進行排序。
值得一提的是,unordered_map 容器在頭文件中,并位于 std 命名空間中。因此,如果想使用該容器,代碼中應(yīng)包含如下語句:
include using namespace std;
注意,第二行代碼不是必需的,但如果不用,則后續(xù)程序中在使用此容器時,需手動注明 std 命名空間(強烈建議初學者使用)。
unordered_map 容器模板的定義如下所示:
template < class Key, //鍵值對中鍵的類型
class T, //鍵值對中值的類型
class Hash = hash, //容器內(nèi)部存儲鍵值對所用的哈希函數(shù)
class Pred = equal_to, //判斷各個鍵值對鍵相同的規(guī)則
class Alloc = allocator< pairnst Key,T> > // 指定分配器對象的類型
> class unordered_map;
以上 5 個參數(shù)中,必須顯式給前 2 個參數(shù)傳值,并且除特殊情況外,最多只需要使用前 4 個參數(shù),各自的含義和功能如表 1 所示。
表 1 unordered_map 容器模板類的常用參數(shù)

總的來說,當無序容器中存儲鍵值對的鍵為自定義類型時,默認的哈希函數(shù) hash 以及比較函數(shù) equal_to 將不再適用,只能自己設(shè)計適用該類型的哈希函數(shù)和比較函數(shù),并顯式傳遞給 Hash 參數(shù)和 Pred 參數(shù)。至于如何實現(xiàn)自定義,后續(xù)章節(jié)會做詳細講解。
創(chuàng)建C++ unordered_map容器的方法
常見的創(chuàng)建 unordered_map 容器的方法有以下幾種。
1) 通過調(diào)用 unordered_map 模板類的默認構(gòu)造函數(shù),可以創(chuàng)建空的 unordered_map 容器。比如:
std::unordered_map umap;
由此,就創(chuàng)建好了一個可存儲 類型鍵值對的 unordered_map 容器。
2) 當然,在創(chuàng)建 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;} };
通過此方法創(chuàng)建的 umap 容器中,就包含有 3 個鍵值對元素。
3) 另外,還可以調(diào)用 unordered_map 模板中提供的復(fù)制(拷貝)構(gòu)造函數(shù),將現(xiàn)有 unordered_map 容器中存儲的鍵值對,復(fù)制給新建 unordered_map 容器。
例如,在第二種方式創(chuàng)建好 umap 容器的基礎(chǔ)上,再創(chuàng)建并初始化一個 umap2 容器:
std::unordered_map umap2(umap);
由此,umap2 容器中就包含有 umap 容器中所有的鍵值對。
除此之外,C++ 11 標準中還向 unordered_map 模板類增加了移動構(gòu)造函數(shù),即以右值引用的方式將臨時 unordered_map 容器中存儲的所有鍵值對,全部復(fù)制給新建容器。例如:
//返回臨時 unordered_map 容器的函數(shù)
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;}
//調(diào)用移動構(gòu)造函數(shù),創(chuàng)建 umap2 容器
std::unordered_map umap2(retUmap());
注意,無論是調(diào)用復(fù)制構(gòu)造函數(shù)還是拷貝構(gòu)造函數(shù),必須保證 2 個容器的類型完全相同。
4) 當然,如果不想全部拷貝,可以使用 unordered_map 類模板提供的迭代器,在現(xiàn)有 unordered_map 容器中選擇部分區(qū)域內(nèi)的鍵值對,為新建 unordered_map 容器初始化。例如:
//傳入 2 個迭代器,
std::unordered_map
umap2(++umap.begin(),umap.end());
通過此方式創(chuàng)建的 umap2 容器,其內(nèi)部就包含 umap 容器中除第 1 個鍵值對外的所有其它鍵值對。
C++ unordered_map容器的成員方法
unordered_map 既可以看做是關(guān)聯(lián)式容器,更屬于自成一脈的無序容器。因此在該容器模板類中,既包含一些在學習關(guān)聯(lián)式容器時常見的成員方法,還有一些屬于無序容器特有的成員方法。
表 2 列出了 unordered_map 類模板提供的所有常用的成員方法以及各自的功能。


注意,對于實現(xiàn)互換 2 個相同類型 unordered_map 容器的鍵值對,除了可以調(diào)用該容器模板類中提供的 swap() 成員方法外,STL 標準庫還提供了同名的 swap() 非成員函數(shù)。
下面的樣例演示了表 2 中部分成員方法的用法:
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空 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 存儲鍵值對的數(shù)量
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;
}
程序執(zhí)行結(jié)果為:
umap size = 3
Python教程 http://c.biancheng.net/python/
Linux教程 http://c.biancheng.net/linux/
Java教程 http://c.biancheng.net/java/
C++ STL無序容器底層實現(xiàn)原理(深度剖析)
在了解哈希表存儲結(jié)構(gòu)的基礎(chǔ)上,本節(jié)將具體分析 C++ STL 無序容器(哈希容器)底層的實現(xiàn)原理。
C++ STL 標準庫中,不僅是 unordered_map 容器,所有無序容器的底層實現(xiàn)都采用的是哈希表存儲結(jié)構(gòu)。更準確地說,是用“鏈地址法”(又稱“開鏈法”)解決數(shù)據(jù)存儲位置發(fā)生沖突的哈希表,整個存儲結(jié)構(gòu)如圖 1 所示。

其中,Pi 表示存儲的各個鍵值對。
可以看到,當使用無序容器存儲鍵值對時,會先申請一整塊連續(xù)的存儲空間,但此空間并不用來直接存儲鍵值對,而是存儲各個鏈表的頭指針,各鍵值對真正的存儲位置是各個鏈表的節(jié)點。
注意,STL 標準庫通常選用 vector 容器存儲各個鏈表的頭指針。
不僅如此,在 C++ STL 標準庫中,將圖 1 中的各個鏈表稱為桶(bucket),每個桶都有自己的編號(從 0 開始)。當有新鍵值對存儲到無序容器中時,整個存儲過程分為如下幾步:
- 將該鍵值對中鍵的值帶入設(shè)計好的哈希函數(shù),會得到一個哈希值(一個整數(shù),用 H 表示);
- 將 H 和無序容器擁有桶的數(shù)量 n 做整除運算(即 H % n),該結(jié)果即表示應(yīng)將此鍵值對存儲到的桶的編號;
- 建立一個新節(jié)點存儲此鍵值對,同時將該節(jié)點鏈接到相應(yīng)編號的桶上。
另外值得一提的是,哈希表存儲結(jié)構(gòu)還有一個重要的屬性,稱為負載因子(load factor)。該屬性同樣適用于無序容器,用于衡量容器存儲鍵值對的空/滿程序,即負載因子越大,意味著容器越滿,即各鏈表中掛載著越多的鍵值對,這無疑會降低容器查找目標鍵值對的效率;反之,負載因子越小,容器肯定越空,但并不一定各個鏈表中掛載的鍵值對就越少。
舉個例子,如果設(shè)計的哈希函數(shù)不合理,使得各個鍵值對的鍵帶入該函數(shù)得到的哈希值始終相同(所有鍵值對始終存儲在同一鏈表上)。這種情況下,即便增加桶數(shù)是的負載因子減小,該容器的查找效率依舊很差。
無序容器中,負載因子的計算方法為:
負載因子 = 容器存儲的總鍵值對 / 桶數(shù)
默認情況下,無序容器的最大負載因子為 1.0。如果操作無序容器過程中,使得最大復(fù)雜因子超過了默認值,則容器會自動增加桶數(shù),并重新進行哈希,以此來減小負載因子的值。需要注意的是,此過程會導(dǎo)致容器迭代器失效,但指向單個鍵值對的引用或者指針仍然有效。
這也就解釋了,為什么我們在操作無序容器過程中,鍵值對的存儲順序有時會“莫名”的發(fā)生變動。
C++ STL 標準庫為了方便用戶更好地管控無序容器底層使用的哈希表存儲結(jié)構(gòu),各個無序容器的模板類中都提供表 2 所示的成員方法。
表 2 無序容器管理哈希表的成員方法

下面的程序以學過的 unordered_map 容器為例,演示了表 2 中部分成員方法的用法:
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空 umap 容器
unordered_map umap;
cout << &34;umap 初始桶數(shù): &34; << umap.bucket_count() << endl;
cout << &34;umap 初始負載因子: &34; << umap.load_factor() << endl;
cout << &34;umap 最大負載因子: &34; << umap.max_load_factor() << endl;
//設(shè)置 umap 使用最適合存儲 9 個鍵值對的桶數(shù)
umap.reserve(9);
cout << &34;*********************&34; << endl;
cout << &34;umap 新桶數(shù): &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;;
//調(diào)用 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;
}
程序執(zhí)行結(jié)果為:
umap 初始桶數(shù): 8
umap 初始負載因子: 0
umap 最大負載因子: 1
*********************
umap 新桶數(shù): 16
umap 新負載因子: 0
以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:9
計算以&34;Python教程&34;為鍵的鍵值對,位于桶的編號為:9
從輸出結(jié)果可以看出,對于空的 umap 容器,初始狀態(tài)下會分配 8 個桶,并且默認最大負載因子為 1.0,但由于其為存儲任何鍵值對,因此負載因子值為 0。
與此同時,程序中調(diào)用 reverse() 成員方法,是 umap 容器的桶數(shù)改為了 16,其最適合存儲 9 個鍵值對。從側(cè)面可以看出,一旦負載因子大于 1.0(9/8 > 1.0),則容器所使用的桶數(shù)就會翻倍式(8、16、32、...)的增加。
程序最后還演示了如何手動計算出指定鍵值對存儲的桶的編號,其計算結(jié)果和使用 bucket() 成員方法得到的結(jié)果是一致的。
C++ unordered_map迭代器的用法
C++ STL 標準庫中,unordered_map 容器迭代器的類型為前向迭代器(又稱正向迭代器)。這意味著,假設(shè) p 是一個前向迭代器,則其只能進行 *p、p++、++p 操作,且 2 個前向迭代器之間只能用 == 和 != 運算符做比較。
在 unordered_map 容器模板中,提供了表 1 所示的成員方法,可用來獲取指向指定位置的前向迭代器。
表 1 C++ unordered_map迭代器相關(guān)成員方法

值得一提的是,equal_range(key) 很少用于 unordered_map 容器,因為該容器中存儲的都是鍵不相等的鍵值對,即便調(diào)用該成員方法,得到的 2 個迭代器所表示的范圍中,最多只包含 1 個鍵值對。事實上,該成員方法更適用于 unordered_multimap 容器(該容器后續(xù)章節(jié)會做詳細講解)。
下面的程序演示了表 1 中部分成員方法的用法。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建 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;
}
程序執(zhí)行結(jié)果為:
umap 存儲的鍵值對包括:
umap.find(&34;Java教程&34;) =
需要注意的是,在操作 unordered_map 容器過程(尤其是向容器中添加新鍵值對)中,一旦當前容器的負載因子超過最大負載因子(默認值為 1.0),該容器就會適當增加桶的數(shù)量(通常是翻一倍),并自動執(zhí)行 rehash() 成員方法,重新調(diào)整各個鍵值對的存儲位置(此過程又稱“重哈?!保诉^程很可能導(dǎo)致之前創(chuàng)建的迭代器失效。
所謂迭代器失效,針對的是那些用于表示容器內(nèi)某個范圍的迭代器,由于重哈希會重新調(diào)整每個鍵值對的存儲位置,所以容器重哈希之后,之前表示特定范圍的迭代器很可能無法再正確表示該范圍。但是,重哈希并不會影響那些指向單個鍵值對元素的迭代器。
舉個例子:
include
include
using namespace std;
int main()
{
//創(chuàng)建 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 范圍內(nèi)的每個鍵值對的鍵的值
for (auto iter = pair.first; iter != pair.second; ++iter) {
cout << iter->first <<&34; &34;;
}
cout << endl;
//手動調(diào)整最大負載因子數(shù)
umap.max_load_factor(3.0);
//手動調(diào)用 rehash() 函數(shù)重哈希
umap.rehash(10);
//重哈希之后,pair 的范圍可能會發(fā)生變化
for (auto iter = pair.first; iter != pair.second; ++iter) {
cout << iter->first << &34; &34;;
}
return 0;
}
程序執(zhí)行結(jié)果為:
49
49 17
觀察輸出結(jié)果不難發(fā)現(xiàn),之前用于表示鍵為 49 的鍵值對所在范圍的 2 個迭代器,重哈希之后表示的范圍發(fā)生了改變。
經(jīng)測試,用于遍歷整個容器的 begin()/end() 和 cbegin()/cend() 迭代器對,重哈希只會影響遍歷容器內(nèi)鍵值對的順序,整個遍歷的操作仍然可以順利完成。
C++ STL unordered_map獲取元素的4種方法(超級詳細)
通過前面的學習我們知道,unordered_map 容器以鍵值對的方式存儲數(shù)據(jù)。為了方便用戶快速地從該類型容器提取出目標元素(也就是某個鍵值對的值),unordered_map 容器類模板中提供了以下幾種方法。
1) unordered_map 容器類模板中,實現(xiàn)了對 [ ] 運算符的重載,使得我們可以像“利用下標訪問普通數(shù)組中元素”那樣,通過目標鍵值對的鍵獲取到該鍵對應(yīng)的值。
舉個例子:
include
include
include
using namespace std;
int main()
{
//創(chuàng)建 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; 對應(yīng)的值
string str = umap[&34;Java教程&34;];
cout << str << endl;
return 0;
}
程序輸出結(jié)果為:
http://c.biancheng.net/java/
需要注意的是,如果當前容器中并沒有存儲以 [ ] 運算符內(nèi)指定的元素作為鍵的鍵值對,則此時 [ ] 運算符的功能將轉(zhuǎn)變?yōu)椋?strong>向當前容器中添加以目標元素為鍵的鍵值對。舉個例子:
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空 umap 容器
unordered_map umap;
//[] 運算符在 = 右側(cè)
string str = umap[&34;STL教程&34;];
//[] 運算符在 = 左側(cè)
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;
}
程序執(zhí)行結(jié)果為:
C教程 http://c.biancheng.net/c/
STL教程
可以看到,當使用 [ ] 運算符向 unordered_map 容器中添加鍵值對時,分為 2 種情況:
- 當 [ ] 運算符位于賦值號(=)右側(cè)時,則新添加鍵值對的鍵為 [ ] 運算符內(nèi)的元素,其值為鍵值對要求的值類型的默認值(string 類型默認值為空字符串);
- 當 [ ] 運算符位于賦值號(=)左側(cè)時,則新添加鍵值對的鍵為 [ ] 運算符內(nèi)的元素,其值為賦值號右側(cè)的元素。
2) unordered_map 類模板中,還提供有 at() 成員方法,和使用 [ ] 運算符一樣,at() 成員方法也需要根據(jù)指定的鍵,才能從容器中找到該鍵對應(yīng)的值;不同之處在于,如果在當前容器中查找失敗,該方法不會向容器中添加新的鍵值對,而是直接拋出out_of_range異常。
舉個例子:
include
include
include
using namespace std;
int main(){
//創(chuàng)建 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;} };
//獲取指定鍵對應(yīng)的值
string str = umap.at(&34;Python教程&34;);
cout << str << endl;
//執(zhí)行此語句會拋出 out_of_range 異常
//cout << umap.at(&34;GO教程&34;);
return 0;}
程序執(zhí)行結(jié)果為:
http://c.biancheng.net/python/
此程序中,第 13 行代碼用于獲取 umap 容器中鍵為“Python教程”對應(yīng)的值,由于 umap 容器確實有符合條件的鍵值對,因此可以成功執(zhí)行;而第 17 行代碼,由于當前 umap 容器沒有存儲以“Go教程”為鍵的鍵值對,因此執(zhí)行此語句會拋出 out_of_range 異常。
3) [ ] 運算符和 at() 成員方法基本能滿足大多數(shù)場景的需要。除此之外,還可以借助 unordered_map 模板中提供的 find() 成員方法。
和前面方法不同的是,通過 find() 方法得到的是一個正向迭代器,該迭代器的指向分以下 2 種情況:
- 當 find() 方法成功找到以指定元素作為鍵的鍵值對時,其返回的迭代器就指向該鍵值對;
- 當 find() 方法查找失敗時,其返回的迭代器和 end() 方法返回的迭代器一樣,指向容器中最后一個鍵值對之后的位置。
舉個例子:
include
include
include
using namespace std;
int main(){
//創(chuàng)建 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;}
程序執(zhí)行結(jié)果為:
Python教程 http://c.biancheng.net/python/
當前容器中沒有以&34;GO教程&34;為鍵的鍵值對
4) 除了 find() 成員方法之外,甚至可以借助 begin()/end() 或者 cbegin()/cend(),通過遍歷整個容器中的鍵值對來找到目標鍵值對。
舉個例子:
include
include
include
using namespace std;
int main()
{
//創(chuàng)建 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;
}
程序執(zhí)行結(jié)果為:
http://c.biancheng.net/java/
以上 4 種方法中,前 2 種方法基本能滿足多數(shù)場景的需要,建議初學者首選 at() 成員方法!
C++ unordered_map insert()用法
為了方便用戶向已建 unordered_map 容器中添加新的鍵值對,該容器模板中提供了 insert() 方法,本節(jié)就對此方法的用法做詳細的講解。
unordered_map 模板類中,提供了多種語法格式的 insert() 方法,根據(jù)功能的不同,可劃分為以下幾種用法。
1) insert() 方法可以將 pair 類型的鍵值對元素添加到 unordered_map 容器中,其語法格式有 2 種:
//以普通方式傳遞參數(shù)
pair insert ( const value_type& val );
//以右值引用的方式傳遞參數(shù)
template
pair insert ( P&& val );
為了方便用戶向已建 unordered_map 容器中添加新的鍵值對,該容器模板中提供了 insert() 方法,本節(jié)就對此方法的用法做詳細的講解。
unordered_map 模板類中,提供了多種語法格式的 insert() 方法,根據(jù)功能的不同,可劃分為以下幾種用法。
1) insert() 方法可以將 pair 類型的鍵值對元素添加到 unordered_map 容器中,其語法格式有 2 種:
//以普通方式傳遞參數(shù)
pair insert ( const value_type& val );
//以右值引用的方式傳遞參數(shù)
template
pair insert ( P&& val );
以上 2 種格式中,參數(shù) val 表示要添加到容器中的目標鍵值對元素;該方法的返回值為 pair類型值,內(nèi)部包含一個 iterator 迭代器和 bool 變量:
- 當 insert() 將 val 成功添加到容器中時,返回的迭代器指向新添加的鍵值對,bool 值為 True;
- 當 insert() 添加鍵值對失敗時,意味著當前容器中本就存儲有和要添加鍵值對的鍵相等的鍵值對,這種情況下,返回的迭代器將指向這個導(dǎo)致插入操作失敗的迭代器,bool 值為 False。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空 umap 容器
unordered_map umap;
//構(gòu)建要添加的鍵值對
std::pairmypair(&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//創(chuàng)建接收 insert() 方法返回值的pair類型變量
std::pair::iterator, bool> ret;
//調(diào)用 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;
//調(diào)用 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;
}
程序執(zhí)行結(jié)果為:
bool = 1
iter -> STL教程 http://c.biancheng.net/stl/
bool = 1
iter -> Python教程 http://c.biancheng.net/python/
從輸出結(jié)果很容易看出,兩次添加鍵值對的操作,insert() 方法返回值中的 bool 變量都為 1,表示添加成功,此時返回的迭代器指向的是添加成功的鍵值對。
2) 除此之外,insert() 方法還可以指定新鍵值對要添加到容器中的位置,其語法格式如下:
//以普通方式傳遞 val 參數(shù)
iterator insert ( const_iterator hint, const value_type& val );
//以右值引用方法傳遞 val 參數(shù)
template
iterator insert ( const_iterator hint, P&& val );
以上 2 種語法格式中,hint 參數(shù)為迭代器,用于指定新鍵值對要添加到容器中的位置;val 參數(shù)指的是要添加容器中的鍵值對;方法的返回值為迭代器:
- 如果 insert() 方法成功添加鍵值對,該迭代器指向新添加的鍵值對;
- 如果 insert() 方法添加鍵值對失敗,則表示容器中本就包含有相同鍵的鍵值對,該方法返回的迭代器就指向容器中鍵相同的鍵值對;
注意,以上 2 種語法格式中,雖然通過 hint 參數(shù)指定了新鍵值對添加到容器中的位置,但該鍵值對真正存儲的位置,并不是 hint 參數(shù)說了算,最終的存儲位置仍取決于該鍵值對的鍵的值。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空 umap 容器
unordered_map umap;
//構(gòu)建要添加的鍵值對
std::pairmypair(&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//創(chuàng)建接收 insert() 方法返回值的迭代器類型變量
unordered_map::iterator iter;
//調(diào)用第一種語法格式
iter = umap.insert(umap.begin(), mypair);
cout << &34;iter -> &34; << iter->first <<&34; &34; << iter->second << endl;
//調(diào)用第二種語法格式
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;
}
程序輸出結(jié)果為:
iter -> STL教程 http://c.biancheng.net/stl/
iter -> Python教程 http://c.biancheng.net/python/
3) insert() 方法還支持將某一個 unordered_map 容器中指定區(qū)域內(nèi)的所有鍵值對,復(fù)制到另一個 unordered_map 容器中,其語法格式如下:
template
void insert ( InputIterator first, InputIterator last );
其中 first 和 last 都為迭代器,[first, last)表示復(fù)制其它 unordered_map 容器中鍵值對的區(qū)域。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建并初始化 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;} };
//創(chuàng)建一個空的 unordered_map 容器
unordered_map otherumap;
//指定要拷貝 umap 容器中鍵值對的范圍
unordered_map::iterator first = ++umap.begin();
unordered_map::iterator last = umap.end();
//將指定 umap 容器中 [first,last) 區(qū)域內(nèi)的鍵值對復(fù)制給 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;
}
程序輸出結(jié)果為:
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
4) 除了以上 3 種方式,insert() 方法還支持一次向 unordered_map 容器添加多個鍵值對,其語法格式如下:
void insert ( initializer_list il );
其中,il 參數(shù)指的是可以用初始化列表的形式指定多個鍵值對元素。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建空的 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;
}
程序輸出結(jié)果為:
STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
C++ unordered_map emplace()和emplace_hint()方法
和前面學的 map、set 等容器一樣,C++ 11 標準也為 unordered_map 容器新增了 emplace() 和 emplace_hint() 成員方法,本節(jié)將對它們的用法做詳細的介紹。
我們知道,實現(xiàn)向已有 unordered_map 容器中添加新鍵值對,可以通過調(diào)用 insert() 方法,但其實還有更好的方法,即使用 emplace() 或者 emplace_hint() 方法,它們完成“向容器中添加新鍵值對”的效率,要比 insert() 方法高。
unordered_map emplace()方法
emplace() 方法的用法很簡單,其語法格式如下:
template
pair emplace ( Args&&... args );
其中,參數(shù) args 表示可直接向該方法傳遞創(chuàng)建新鍵值對所需要的 2 個元素的值,其中第一個元素將作為鍵值對的鍵,另一個作為鍵值對的值。也就是說,該方法無需我們手動創(chuàng)建鍵值對,其內(nèi)部會自行完成此工作。
另外需要注意的是,該方法的返回值為 pair 類型值,其包含一個迭代器和一個 bool 類型值:
- 當 emplace() 成功添加新鍵值對時,返回的迭代器指向新添加的鍵值對,bool 值為 True;
- 當 emplace() 添加新鍵值對失敗時,說明容器中本就包含一個鍵相等的鍵值對,此時返回的迭代器指向的就是容器中鍵相同的這個鍵值對,bool 值為 False。
include
include
include
using namespace std;
int main()
{
//創(chuàng)建 umap 容器
unordered_map umap;
//定義一個接受 emplace() 方法的 pair 類型變量
pair::iterator, bool> ret;
//調(diào)用 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;
}
程序執(zhí)行結(jié)果為:
bool =1
iter ->STL教程 http://c.biancheng.net/stl/
通過執(zhí)行結(jié)果中 bool 變量的值為 1 可以得知,emplace() 方法成功將新鍵值對添加到了 umap 容器中。
unordered_map emplace_hint()方法
emplace_hint() 方法的語法格式如下:
template
iterator emplace_hint ( const_iterator position, Args&&... args );
和 emplace() 方法相同,emplace_hint() 方法內(nèi)部會自行構(gòu)造新鍵值對,因此我們只需向其傳遞構(gòu)建該鍵值對所需的 2 個元素(第一個作為鍵,另一個作為值)即可。不同之處在于:
- emplace_hint() 方法的返回值僅是一個迭代器,而不再是 pair 類型變量。當該方法將新鍵值對成功添加到容器中時,返回的迭代器指向新添加的鍵值對;反之,如果添加失敗,該迭代器指向的是容器中和要添加鍵值對鍵相同的那個鍵值對。
- emplace_hint() 方法還需要傳遞一個迭代器作為第一個參數(shù),該迭代器表明將新鍵值對添加到容器中的位置。需要注意的是,新鍵值對添加到容器中的位置,并不是此迭代器說了算,最終仍取決于該鍵值對的鍵的值。
可以這樣理解,emplace_hint() 方法中傳入的迭代器,僅是給 unordered_map 容器提供一個建議,并不一定會被容器采納。
舉個例子:
include
include
include
using namespace std;
int main(){
//創(chuàng)建 umap 容器
unordered_map umap;
//定義一個接受 emplace_hint() 方法的迭代器
unordered_map::iterator iter;
//調(diào)用 empalce_hint() 方法
iter = umap.emplace_hint(umap.begin(),&34;STL教程&34;, &34;http://c.biancheng.net/stl/&34;);
//輸出 emplace_hint() 返回迭代器 iter 指向的鍵值對的內(nèi)容
cout << &34;iter ->&34; << iter->first << &34; &34; << iter->second << endl;
return 0;
}
程序執(zhí)行結(jié)果為:
iter ->STL教程 http://c.biancheng.net/stl/