【STL】關聯容器之unordered_map用法總結
一、基本原理
unordered_map是C++標準庫提供的關聯容器之一,儲存的是鍵值對(key-value),我們可以通過key快速查詢到其對應的value。unordered_map底層使用的資料結構是雜湊表(hash table),因此在unordered_map中查詢、新增或刪除元素時間複雜度都是常數時間O(1)。此外,unordered_map中的元素是無序的。
unordered_map使用場景:
現在假設有一個寢室501,寢室有4個學生,我們要用一種容器來儲存這4個學生的學號和姓名,需求是我要通過學號來查詢這個學號對應的姓名。
此時我們用一個unordered_map來儲存,學號作為key,姓名作為value,一個學生的這兩個資訊作為unordered_map的一個元素pair(pair用法總結),我們可以通過學號來得到對應的姓名。
也許你會有這樣的疑問,為什麼要用unordered_map呢,我用一個二維陣列不是也一樣能存放寢室4個學生的學號和姓名嗎?
是的,存是可以存,但是操作的時間複雜度就不一樣了,比如我知道學號"sc0303",我想查詢對應的姓名,如果用unordered_map儲存的,根據雜湊表的特性,只需要O(1)的時間就能查到對應的姓名。如果用的二維陣列儲存的,需要從頭遍歷二維陣列的第一列,需要O(n)的時間才能查到。這就是使用unordered_map的意義。
也許你還會有這樣的疑問,為什麼要用unordered_map呢,我用一個map也一樣能存嗎?
用map和unordered_map的區別在於,map基於紅黑樹,是按關鍵字有序排列的,unordered_map基於雜湊表,是雜湊存放的(無序)。當你需要進行範圍查詢“比學號sc0302大的同學的姓名”,那麼使用map就更方便,因為map中元素已經按照學號(關鍵字)有序排好了,但是如果這種場景下使用unordered_map,你要進行範圍查詢只能一個一個去比較了。在進行等值查詢時unordered_map只需要O(1)的時間就能查到,但map卻需要O(log(n))的時間,因此等值查詢時使用unordered_map更快。
具體選用哪種容器還是要看使用場景,它們各有優勢。
二、用法
unordered_map中的元素是一對對鍵值對,型別pair,用法可參考部落格pair用法總結
初始化
unordered_map<T1,T2> 容器名; | T1、T2是型別名,可以是基本資料型別int、double等,也可以是類型別string等 |
---|---|
unordered_map<T1,T2> m1; | 建立一個名為m1的空unordered_map,key的型別為T1,value的型別為T2 |
unordered_map<T1,T2> m1{p1,p2……}; | m1中的元素被初始化為p1,p2……,p1、p2是pair型別 |
unordered_map<T1,T2> m1{{key1,value1},{key2,value2}……}; | m1中的元素被初始化為 鍵值對{key1,value1},{key2,value2}…… |
unordered_map<T1,T2> m2(m1); | m2中包含和m1一樣的元素 |
unordered_map<T1,T2> m2=m1; | m2中包含和m1一樣的元素 |
程式示例:
pair<string, string> p1("sc0301","小楊"); // 方式一,建立一個pair名為p1
pair<string, string> p2 = make_pair("sc0302", "小馬"); // 方式二,make_pair函式返回一個用"sc0302"和 "小馬"初始化的pair
pair<string, string> p3("sc0303", "小王");
pair<string, string> p4("sc0304", "小何");
unordered_map<string, string> m1; // 建立一個空unordered_map
unordered_map<string, string> m2{ p1,p2,p3,p4 }; // 建立一個包含鍵值對p1、p2、p3、p4的unordered_map
unordered_map<string, string> m3{ {"sc0301","小楊"},{"sc0302", "小馬"},{"sc0303", "小王"},{"sc0304", "小何"} }; // 效果同上一句
unordered_map<string, string> m4(m2); // 建立一個unordered_map,m4中包含和m2一樣的元素
unordered_map<string, string> m5 = m2; // 建立一個unordered_map,m5中包含和m2一樣的元素
訪問元素
訪問元素 | |
---|---|
T2 value = m1[key]; | 得到關鍵字key對應的值value |
m1.at(key); | 得到關鍵字key對應的值value |
*iter | 訪問迭代器iter指向的元素 |
獲取迭代器 | |
---|---|
m1.begin(); | 獲取指向m1首元素的迭代器 |
m1.end(); | 獲取指向m1尾元素的後一個位置的迭代器 |
m1.rbegin(); | 獲取指向m1首元素的前一個位置的迭代器 |
m1.rend(); | 獲取指向m1尾元素的迭代器 |
m1.cbegin(); m1.cend(); | 含義同上,但獲取到的是const_iterator |
m1.crbegin(); m1.crend(); | 含義同上,但獲取到的是const_iterator |
程式示例:
string p2_name = m2["sc0302"]; // 得到學號(關鍵字)"sc0302"對應的姓名(值)
string p3_name = m2.at("sc0303"); // 得到學號"sc0303"對應的姓名
unordered_map<string, string>::iterator it1 = m2.begin(); // 得到指向m2首元素的迭代器
unordered_map<string, string>::iterator it2 = m2.end(); // 得到指向m2尾元素的下一個位置的迭代器
pair<string, string> p11 = *it1; // 得到m2的首元素{"sc0301","小楊"}
string p1_ID = it1->first; // 得到m2的首元素{"sc0301","小楊"}的fisrt成員
string p1_name = it1->second; // 得到m2的首元素{"sc0301","小楊"}的second成員
遍歷容器
1、使用迭代器
for (auto it_b = m2.begin(), it_e = m2.end(); it_b != it_e;++it_b) {
pair<string, string> current = *it_b;
cout << "學號:" << it_b->first << "; 姓名:" << it_b->second << endl;
// 上面這一句等同於下面這一句
cout << "學號:" << current.first << "; 姓名:" << current.second << endl;
}
2、範圍for語句
for (auto p : m2) {
cout << "學號:" << p.first << "; 姓名:" << p.second << endl;
}
新增、修改元素
m1.insert(p1); | 在unordered_map中插入已有的pair |
---|---|
m1.insert({key1,value1}); | 在unordered_map中插入鍵值對{key1,value1} |
m1.insert(pair<T1, T2> (key1, value1)); | // 建立一個無名pair物件,並插入到unordered_map中 |
m1.insert(make_pair(key1, value1)); | // 建立一個無名pair物件,並插入到unordered_map中 |
m1.insert(unordered_map<T1, T2>::value_type (key1, value1)); | // 建立一個無名pair物件,並插入到unordered_map中 |
m1[key] = value; | 如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value |
m1.emplace(p1); | 在unordered_map中插入已有的pair |
m1.emplace(pair<T1, T2> (key1, value1)); | // 建立一個無名pair物件,並插入到unordered_map中 |
程式示例:
m1.insert(p1); // 在unordered_map中插入已有的pair
m1.insert({ "sc0302", "小馬" }); // 插入鍵值對{ "sc0302", "小馬" }
m1.insert(pair<string, string> ("sc0303", "小王")); // 建立一個無名pair物件,並插入到unordered_map中
m1.insert(make_pair("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.insert(unordered_map<string, string>::value_type("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1["sc0304"] = "小何"; // 如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value。
m1.emplace(p1); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.emplace(pair<string, string>("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
刪除元素
m1.erase(key) | 刪除關鍵字為key的元素 |
---|---|
m1.erase(iter); | 刪除迭代器iter指向的元素 |
m1.erase(iter1,iter2); | 刪除迭代器iter1和ter2指向的範圍中元素 |
m1.clear(); | 刪除m1中的所有元素 |
程式示例:
m1.erase("sc0301"); // 刪除關鍵字為"sc0301"的元素
auto iter = m1.begin(); // 指向m2的首元素的迭代器
m1.erase(iter); // 刪除迭代器指向的元素
auto iter1 = m1.begin(), iter2 = m1.end();
m1.erase(iter1, iter2); // 刪除迭代器iter1、iter2指向的範圍中的元素
m2.clear(); // 刪除容器中的全部元素
unordered_map的大小
m1.szie(); | 獲得m1的大小 |
---|---|
m1.empty(); | 若m1為空返回值為true,否則返回值為false |
程式示例:
int s = m3.size(); // 獲得m1的大小
bool e = m3.empty(); // 若m1為空返回值為true,否則返回值為false
查詢
m1.find(key) | 返回一個迭代器,指向第一個關鍵字為key的元素,若key不在容器中,則返回尾後迭代器 |
---|---|
m1.count(key); | 返回關鍵字等於key的元素的數量。對於不允許重複關鍵字的容器unordered_map,返回的值是0或1 |
程式示例:
unordered_map<string, string>::iterator it = m4.find("sc0301"); // 查詢關鍵字為"sc0301"的元素,返回一個迭代器
if (it == m4.end()) { // 若"sc0301"不在容器中,則it等於尾後迭代器
cout << "未找到!" << endl;
}
else {
pair<string, string> result1 = *it; // 找到了
}
int result2 = m4.count("sc0305"); // 查詢關鍵字為"sc0301"的元素,返回關鍵字等於"sc0301"的元素數量
if (result2==0) {
cout << "未找到!" << endl;
}
else {
cout << "找到了!" << endl;
}
unordered_map<string, string>::iterator it_up = m4.upper_bound("sc0301"); // 返回一個迭代器,指向第一個關鍵字大於"sc0301"的元素
pair<string, string> result3 = *it_up;
三、程式示例
#include <iostream>
#include <string>
#include <utility>
#include <unordered_map>
using namespace std;
int main() {
// 初始化-----------------------------------------------------------------------------------
pair<string, string> p1("sc0301","小楊"); // 方式一,建立一個pair名為p1
pair<string, string> p2 = make_pair("sc0302", "小馬"); // 方式二,make_pair函式返回一個用"sc0302"和 "小馬"初始化的pair
pair<string, string> p3("sc0303", "小王");
pair<string, string> p4("sc0304", "小何");
unordered_map<string, string> m1; // 建立一個空unordered_map
unordered_map<string, string> m2{ p1,p2,p3,p4 }; // 建立一個包含鍵值對p1、p2、p3、p4的unordered_map
unordered_map<string, string> m3{ {"sc0301","小楊"},{"sc0302", "小馬"},{"sc0303", "小王"},{"sc0304", "小何"} }; // 效果同上一句
unordered_map<string, string> m4(m2); // 建立一個unordered_map,m4中包含和m2一樣的元素
unordered_map<string, string> m5 = m2; // 建立一個unordered_map,m5中包含和m2一樣的元素
// 訪問元素-----------------------------------------------------------------------------------
string p2_name = m2["sc0302"]; // 得到學號(關鍵字)"sc0302"對應的姓名(值)
string p3_name = m2.at("sc0303"); // 得到學號"sc0303"對應的姓名
unordered_map<string, string>::iterator it1 = m2.begin(); // 得到指向m2首元素的迭代器
unordered_map<string, string>::iterator it2 = m2.end(); // 得到指向m2尾元素的下一個位置的迭代器
pair<string, string> p11 = *it1; // 得到m2的首元素{"sc0301","小楊"}
string p1_ID = it1->first; // 得到m2的首元素{"sc0301","小楊"}的fisrt成員
string p1_name = it1->second; // 得到m2的首元素{"sc0301","小楊"}的second成員
// 遍歷容器-----------------------------------------------------------------------------------
for (auto it_b = m2.begin(), it_e = m2.end(); it_b != it_e;++it_b) {
pair<string, string> current = *it_b;
cout << "學號:" << it_b->first << "; 姓名:" << it_b->second << endl;
// 上面這一句等同於下面這一句
cout << "學號:" << current.first << "; 姓名:" << current.second << endl;
}
for (auto p : m2) {
cout << "學號:" << p.first << "; 姓名:" << p.second << endl;
}
// 新增、修改元素-----------------------------------------------------------------------------------
m1.insert(p1); // 在unordered_map中插入已有的pair
m1.insert({ "sc0302", "小馬" }); // 插入鍵值對{ "sc0302", "小馬" }
m1.insert(pair<string, string> ("sc0303", "小王")); // 建立一個無名pair物件,並插入到unordered_map中
m1.insert(make_pair("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.insert(unordered_map<string, string>::value_type("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1["sc0304"] = "小何"; // 如果key不存在則插入該鍵值對,如果key已存在則修改該key對應的value。
m1.emplace(p1); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
m1.emplace(pair<string, string>("sc0303", "小王")); // 要插入的關鍵字已在容器中,emplace/insert什麼都不做
// 刪除元素-----------------------------------------------------------------------------------
m1.erase("sc0301"); // 刪除關鍵字為"sc0301"的元素
auto iter = m1.begin(); // 指向m2的首元素的迭代器
m1.erase(iter); // 刪除迭代器指向的元素
auto iter1 = m1.begin(), iter2 = m1.end();
m1.erase(iter1, iter2); // 刪除迭代器iter1、iter2指向的範圍中的元素
m2.clear(); // 刪除容器中的全部元素
// 大小-----------------------------------------------------------------------------------
int s = m3.size(); // 獲得m1的大小
bool e = m3.empty(); // 若m1為空返回值為true,否則返回值為false
// 查詢-----------------------------------------------------------------------------------
unordered_map<string, string>::iterator it = m4.find("sc0301"); // 查詢關鍵字為"sc0301"的元素,返回一個迭代器
if (it == m4.end()) { // 若"sc0301"不在容器中,則it等於尾後迭代器
cout << "未找到!" << endl;
}
else {
pair<string, string> result1 = *it; // 找到了
}
int result2 = m4.count("sc0305"); // 查詢關鍵字為"sc0301"的元素,返回關鍵字等於"sc0301"的元素數量
if (result2==0) {
cout << "未找到!" << endl;
}
else {
cout << "找到了!" << endl;
}
}