本篇將介紹如何使用 C++ std map 以及用法,C++ std::map 是一個關聯式容器,關聯式容器把鍵值和一個元素連繫起來,並使用該鍵值來尋找元素、插入元素和刪除元素等操作。
map 是有排序關聯式容器,即 map 容器中所有的元素都會根據元素對應的鍵值來排序,而鍵值 key 是唯一值,並不會出現同樣的鍵值 key,也就是說假設已經有一個 鍵值 key 存在 map 裡,當同樣的鍵值 key 再 insert 資料時,新的資料就會覆蓋掉原本 key 的資料。
map 的實作方式通常是用紅黑樹(red-black tree)實作的,這樣它可以保證可以在O(log n)
時間內完成搜尋、插入、刪除,n為元素的數目。
以下內容將分為這幾部分,
- map 常用功能
- map 初始化
- map 容器插入元素與存取元素
- map 容器的迴圈遍歷
- 使用 string 當 key 鍵值, int 當 value 資料的 map 範例
- 使用 string 當 key 鍵值, 自定義類別或自定義結構當 value 資料的 map 範例
- 刪除 map 指定的元素
- 清空 map 容器
- 判斷 map 容器是否為空
要使用 map 容器的話,需要引入的標頭檔:<map>
map 常用功能
C++ map 是一種關聯式容器,包含「key鍵值/value資料」成對關係
元素存取operator[]
:存取指定的[i]元素的資料
迭代器begin()
:回傳指向map頭部元素的迭代器end()
:回傳指向map末尾的迭代器rbegin()
:回傳一個指向map尾部的反向迭代器rend()
:回傳一個指向map頭部的反向迭代器
容量empty()
:檢查容器是否為空,空則回傳truesize()
:回傳元素數量max_size()
:回傳可以容納的最大元素個數
修改器clear()
:刪除所有元素insert()
:插入元素erase()
:刪除一個元素swap()
:交換兩個map
查找count()
:回傳指定元素出現的次數find()
:查找一個元素
map 初始化
接下來說說怎麼初始化 c++ map 容器吧!
先以 int 當 key, string 當 value 的 map 為範例,
以一個班級的學生編號為例,一個編號會對應到一個學生,這邊先以學生姓名作示範,
所以會寫成std::map<int, std::string> studentMap
這樣
std::map 宣告時要宣告兩個變數類型,
map.first:第一個稱為(key)鍵值,在 map 裡面,(key)鍵值不會重複
map.second:第二個稱為(key)鍵值對應的數值(value)
map 容器初始化的寫法如下,1
2
3
4std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));
studentMap.insert(std::pair<int, std::string>(7, "Jack"));
studentMap.insert(std::pair<int, std::string>(15, "John"));
或者像陣列的方式一樣1
2
3
4std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
或者這樣初始化更專業一點1
2
3
4
5std::map<int, std::string> studentMap = {
{1, "Tom"},
{2, "Jack"},
{3, "John"}
};
map 容器插入元素與存取元素
剛剛在初始化部分已經有示範過了,map 容器插入元素有兩種寫法,
第一種是使用中括號 []
的方式來插入元素,很像陣列的寫法,例如:map[key] = value
,如果該 key 值已經存在則 value 會被更新成新的數值,範例如下,1
2
3
4std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
...
studentMap[1] = "John";
另一種是使用 map.insert()
成員函式來插入元素,
那這個 map.insert()
方式跟上面中括號 []
的方式有什麼不同呢?
差別在於如果 key 值已經存在的話,使用中括號 []
的方式會將新資料覆蓋舊資料,
而使用 map.insert()
的方式會回傳插入的結果,該 key 值存在的話會回傳失敗的結果,
檢查 map.insert()
的插入結果方式如下,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// g++ std-map.cpp -o a.out -std=c++11
int main() {
std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));
std::pair<std::map<int, std::string>::iterator, bool> retPair;
retPair = studentMap.insert(std::pair<int, std::string>(1, "Tom"));
if (retPair.second == true)
std::cout << "Insert Successfully\n";
else
std::cout << "Insert Failure\n";
return 0;
}
要取得某 key 鍵值元素的話可以這樣寫,或者看下節遍歷整個 map 容器1
std::cout << "name: " << studentMap[1] << "\n";
map 容器的迴圈遍歷
迴圈遍歷 map 容器的方式有幾種,以下先介紹使用 range-based for loop 來遍歷 map 容器,
這邊故意將 id 不按順序初始化或者插入,先初始化 1
、3
、2
key 鍵值的元素,
之後再插入 5
和 4
key 鍵值的元素,然後我們再來觀察看看是不是 map 會將其排序,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// g++ std-map2.cpp -o a.out -std=c++11
int main() {
std::map<int, std::string> studentMap = {
{1, "Tom"},
{3, "John"},
{2, "Jack"}
};
studentMap[5] = "Tiffany";
studentMap[4] = "Ann";
for (const auto& s : studentMap) {
std::cout << "id: " << s.first << ", name: " << s.second << "\n";
}
return 0;
}
輸出內容如下,從這個輸出結果發現是 key 鍵值由小到大排列,所以 map 容器裡面真的是會幫你排序的,
在插入元素的同時會根據鍵值來進行排序,1
2
3
4
5id: 1, name: Tom
id: 2, name: Jack
id: 3, name: John
id: 4, name: Ann
id: 5, name: Tiffany
使用前向迭代器,輸出結果跟上列相同,1
2
3
4
5for (std::map<int, std::string>::iterator it = studentMap.begin(); it != studentMap.end(); it++) {
// or
// for (auto it = studentMap.begin(); it != studentMap.end(); it++) {
std::cout << "id: " << (*it).first << ", name: " << (*it).second << "\n";
}
使用反向迭代器的例子,如果嫌 iterator 迭代器名稱太長的話可以善用 auto
關鍵字讓編譯器去推導該變數類型,1
2
3
4
5// for (std::map<int, std::string>::reverse_iterator it = studentMap.rbegin(); it != studentMap.rend(); it++) {
// or
for (auto it = studentMap.rbegin(); it != studentMap.rend(); it++) {
std::cout << "id: " << (*it).first << ", name: " << (*it).second << "\n";
}
反向迭代器的輸出結果如下,反著印出來,1
2
3
4
5id: 5, name: Tiffany
id: 4, name: Ann
id: 3, name: John
id: 2, name: Jack
id: 1, name: Tom
使用 string 當 key 鍵值, int 當 value 資料的 map 範例
這個範例跟之前的範例相反,我們試著用 string 當作 key 鍵值,int 當 value,看看是不是也可以這樣使用,
所以會寫成 std::map<std::string, int>
這樣,完整範例如下,
1 | // g++ std-map3.cpp -o a.out -std=c++11 |
輸出內容如下,可以發現印出來的順序是按照學生姓名的順序,1
2
3
4
5
6
7
8
9
10key: Ann value: 4
key: Jack value: 2
key: Tom value: 1
3
5
key: Ann value: 4
key: Jack value: 2
key: John value: 3
key: Tiffany value: 5
key: Tom value: 1
使用 string 當 key 鍵值, 自定義類別或自定義結構當 value 資料的 map 範例
如果要在 value 欄位放入自定義 struct 或 自定義 class 的話,可以參考下面這個範例,
那個宣告就會是 std::map<std::string, Class/Struct>
這樣子寫,
1 | // g++ std-map4.cpp -o a.out -std=c++11 |
輸出內容如下:1
2
3name: Ann id: 4 age: 20
name: Jack id: 2 age: 16
name: Tom id: 1 age: 18
刪除 map 指定的元素
刪除 map 指定的元素要使用 erase()
,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// g++ std-map5.cpp -o a.out -std=c++11
int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
studentMap.erase(1);
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}
return 0;
}
結果如下,1
27 Jack
15 John
那如果 map 刪除不存在的元素會發生什麼事呢?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// g++ std-map6.cpp -o a.out -std=c++11
int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
auto ret = studentMap.erase(1);
std::cout << ret << "\n";
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}
ret = studentMap.erase(2);
std::cout << ret << "\n";
for (const auto& m : studentMap) {
std::cout << m.first << " " << m.second << "\n";
}
return 0;
}
map 刪除不存在的元素並不會造成什麼 crash 這種嚴重問題,他反而會回傳一個數量告訴你它刪除了多少個元素,以這個例子來說 erase(1)
是刪除了 1 個元素,erase(2)
是刪除了 0 個元素,結果如下,1
2
3
4
5
61
7 Jack
15 John
0
7 Jack
15 John
map erase()
刪除元素還有另外兩種用法,有興趣的可以看這一篇。
清空 map 容器
要清空 map 容器的的話,要使用 clear()
,1
2
3
4
5
6std::map<int, std::string> studentMap;
studentMap.insert(std::pair<int, std::string>(1, "Tom"));
studentMap.insert(std::pair<int, std::string>(7, "Jack"));
studentMap.insert(std::pair<int, std::string>(15, "John"));
studentMap.clear();
判斷 map 容器是否為空
要判斷 map 是否為空或是裡面有沒有元素的話,可以用 empty()
,寫法如下,1
2
3
4
5
6
7
8std::map<int, std::string> studentMap;
studentMap.clear();
if (studentMap.empty()) {
std::cout << "empty\n";
} else {
std::cout << "not empty, size is "<< studentMap.size() <<"\n";
}
參考
std::map - cppreference.com
https://en.cppreference.com/w/cpp/container/map
map - C++ Reference
http://www.cplusplus.com/reference/map/map/
Map in C++ Standard Template Library (STL) - GeeksforGeeks
https://www.geeksforgeeks.org/map-associative-containers-the-c-standard-template-library-stl//
C/C++ - Map (STL) 用法與心得完全攻略 | Mr. Opengate
https://mropengate.blogspot.com/2015/12/cc-map-stl.html
[教學]C++ Map(STL)詳細用法 @ 一個小小工程師的心情抒發天地
http://dangerlover9403.pixnet.net/blog/post/216833943
C++ STL map 的小筆記 @ 伊卡洛斯之翼
http://kamory0931.pixnet.net/blog/post/119251820
C++中的STL中map用法详解 - Boblim - 博客园
https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html
程式扎記: [C++ 範例代碼] 使用 STL 的 map 操作範例
http://puremonkey2010.blogspot.com/2010/08/c-stl-map.html
其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
std::multimap 用法與範例
std::unordered_map 用法與範例
std::vector 用法與範例
std::deque 介紹與用法
std::queue 用法與範例