目录

std::map用法

更详细的教程

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 不按顺序初始化或者插入,先初始化 132 key 键值的元素, 之后再插入 54 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;
}

結果如下,

1
2
7 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
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 用法與範例