更详细的教程
https://www.nhooo.com/cpp/cpp-map.html
本篇將介紹如何使用 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()
:檢查容器是否為空,空則回傳true
size()
:回傳元素數量
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
4
|
std::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
4
|
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
|
或者這樣初始化更專業一點
1
2
3
4
5
|
std::map<int, std::string> studentMap = {
{1, "Tom"},
{2, "Jack"},
{3, "John"}
};
|
map 容器插入元素與存取元素
刚刚在初始化部分已经有示范过了,map 容器插入元素有两种写法,
第一种是使用中括号 []
的方式来插入元素,很像阵列的写法,例如:map[key] = value
,如果该 key 值已经存在则 value 会被更新成新的数值,范例如下,
1
2
3
4
|
std::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
|
std-map.cpp// g++ std-map.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
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容器的大小
size()
1
2
|
map<char, int> m;
cout << "map的初始大小 = " << m.size() << endl;
|
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
|
std-map2.cpp// g++ std-map2.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
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
5
|
id: 1, name: Tom
id: 2, name: Jack
id: 3, name: John
id: 4, name: Ann
id: 5, name: Tiffany
|
使用前向迭代器,输出结果跟上列相同,
1
2
3
4
5
|
for (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
5
|
id: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
std-map3.cpp// g++ std-map3.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
int main() {
std::map<std::string, int> map = {
{"Tom", 1},
{"Jack", 2},
{"Ann", 4}
};
for (const auto& n : map) {
std::cout << "key: " << n.first << " value: " << n.second << "\n";
}
map["Tiffany"] = 5;
map["John"] = 3;
std::cout << map["John"] << "\n";
std::cout << map["Tiffany"] << "\n";
for (const auto& n : map) {
std::cout << "key: " << n.first << " value: " << n.second << "\n";
}
return 0;
}
|
输出内容如下,可以发现印出来的顺序是按照学生姓名的顺序,
1
2
3
4
5
6
7
8
9
10
|
key: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
std-map4.cpp// g++ std-map4.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
struct Student {
int id;
std::string name;
int age;
};
int main() {
std::map<std::string, Student> studentMap;
studentMap["Tom"] = {1, "Tom", 18};
studentMap["Ann"] = {4, "Ann", 20};
studentMap["Jack"] = {2, "Jack", 16};
for (const auto& m : studentMap) {
std::cout << "name: " << m.first << " id: " << m.second.id << " age: " << m.second.age << "\n";
}
return 0;
}
|
輸出內容如下:
1
2
3
|
name: 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
|
std-map5.cpp// g++ std-map5.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
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;
}
|
結果如下,
那如果 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
|
std-map6.cpp// g++ std-map6.cpp -o a.out -std=c++11
#include <iostream>
#include <string>
#include <map>
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
6
|
1
7 Jack
15 John
0
7 Jack
15 John
|
map erase()
刪除元素還有另外兩種用法,有興趣的可以看這一篇。
清空 map 容器
要清空 map 容器的的話,要使用 clear()
,
1
2
3
4
5
6
|
std::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
8
|
std::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 用法與範例