首頁 > 軟體

C++中map 字典的基本使用教學

2021-09-15 16:01:10

前言

在 C++ 中,map 是關聯容器的一種,關聯容器將值與鍵關聯到一起,並使用鍵來查詢值。這與 python 中的字典型別類似,也是用來儲存鍵-值對(key-value) 形式的資料的,其同樣具有遍歷、插入、查詢、刪除等功能,下面將一一介紹。

1、map 型別的宣告

若想在 C++ 中使用 map 型別建立字典,則需要包含 map 標頭檔案,如下:

#include <map>

map 型別的定義如下:

map<KeyType, ValueType> dict;

其中,KeyType 代表鍵的資料型別,ValueType 代表值的資料型別。KeyType 預設是 const 型別,即鍵在確認之後為唯讀資料。另外,在一個 map 資料中,一個鍵只能出現一次;而在 multimap 型別資料中,一個鍵可以出現多次,本次主要討論 map 資料。看下面一個例子:

#include <iostream>
#include <string>
#include <map>
 
using namespace std;
int main(void)
{
    map<string, int> dict{{"key1", 2}};  // 定義一個 map 型別資料 
    dict["key2"] = 3;  // 建立並賦值
    dict["key3"] ;
    cout << "length of dict: " << dict.size() << endl;  // 長度
    cout << dict["key1"] << endl;  // 查詢
    cout << dict["key2"] << endl;
    cout << dict["key3"] << endl;  // 值預設為0 
}
 
//======= 執行結果如下 ========//
length of dict: 2
2
3
0

例子中首先宣告了一個 map 型別資料 dict ,key 的資料型別為 string ,value 的資料型別為 int。初始化時注意一對鍵值對要放在一對花括號{}中,並用逗號隔開:

{key, value}

如果鍵 key 對還未出現在 dict 中,下標運算會建立一個新元素,其值預設為 0 。這裡建立了三個鍵值對,一個key 為 字串 "key1" ,值為 2;key 為 字串 "key3" 的鍵值對由於未被賦值,因此其值預設為 0 。

2、pair 型別

上述 dict 字典中儲存的每一個資料都是 pair 型別的資料,即 map 資料是一群 pair 型別的集合。一個 pair 儲存兩資料成員: first 和 second,分別用來儲存一個鍵值對的 鍵 和 值 ,其中 first 成員是 const 的,這也解釋了上面提到的 鍵在確認之後為唯讀資料。pair 的標準庫型別定義在標頭檔案 utility 中,在定義一個 pair 時,其預設建構函式會對資料成員進行初始化。一個例子如下:

int main(void)
{
    pair<string, int> a_pair{"key", 1};  // 定義一個 pair 並初始化
    cout << a_pair.first << ": " << a_pair.second << endl;
}
 
// =========== 執行結果 =========== //
key: 1

3、map 資料的遍歷

map 資料的遍歷主要有兩種方法,一個是直接遍歷,另一種是使用迭代器的遍歷。

1)直接遍歷:

#include <iostream>
#include <string>
#include <map>
 
using namespace std;
int main(void)
{
    map<string, int> dict;  // 定義一個 map 型別資料
    dict["key1"] = 2;  // 建立並賦值
    dict["key2"] = 3;  // 
    dict["key3"] ;     // 預設值為0
    for(auto c : dict)  // 遍歷,c++11 特有迴圈方式
    {
        cout << c.first << ": " << c.second << endl;
    }
}
//============ 執行結果 ============//
key1: 2
key2: 3
key3: 0

這種遍歷方式就是 for 迴圈直接遍歷。每次迴圈都會從 dict 中提取一個 pair 型別的物件存入 c 中以供後續操作。

2)使用迭代器遍歷

map 支援 vector 中的 begin、end、cbegin、cend 操作,因此我們可以使用這些函數獲取迭代器,然後使用迭代器來遍歷 map。begin、end 指向首元素以及尾元素之後位置(one past the last element)的迭代器,而容器中的元素位於 begin 與 end 之間,不包含 end,即是一個左閉右開的區間:[begin,end)。cbegin、cend 是C++11引入的新標準,返回 const_iterator,只能用來讀取迭代器所指向內容的資料,不能修改資料 。一個使用迭代器遍歷的例子如下:

#include <iostream>
#include <string>
#include <map>
 
using namespace std;
 
int main(void)
{   
    // 定義並初始化一個 map 型別資料 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}};  
    auto map_it = dict.begin();  // 獲取指向首元素的迭代器
    // 判斷範圍,比較當前迭代器和尾後元素迭代器
    while (map_it != dict.end())
    {
        cout << map_it->first << ": " << map_it->second << endl;
        map_it++;  // 迭代器遞增,移動到下一個元素
    }
}

4、新增元素

map 的 insert 成員函數用來新增一個元素或者一個元素範圍,emplace 成員函數用來新增單個元素。使用 insert 方法向 map 中新增元素的方法如下,不過常用的方式就是第一種方式,剩下的仨瞭解下就行。

dict.insert({"key", 1})    // 或者 dict.emplace({"key", 1})
dict.insert(make_pair("key", 1))
dict.insert(pair<string, int>("key", 1))
dict.insert(map<string, int>::value_type("key", 1))

另外對於一個不存在的鍵的單一元素的 insert 或者 emplace 的插入操作會返回一個 pair ,告訴我們是否新增成功,該 pair 的 first 成員是一個迭代器,指向具有給定關鍵字的元素,second 成員是一個 bool 值,指出元素是插入成功還是已存在於 dict 中,如果之前不存在,則返回true;如果先前已存在,則值為 false。

int main(void)
{
    map<string, int> dict{{"key1", 1}};  // 定義一個 map
    auto ret1 = dict.insert({"key2", 1}); // 執行插入操作,若 "key"不存在於dict中,則返回
    cout << "attention: ret1.first is a iterator points the element we insert" << endl;
    cout << ret1.first->first << ":" << ret1.first->second << endl;
    cout << "key2 insert successfully? " << ret1.second << endl;
    auto ret2 = dict.insert({"key1", 3}); //  "key"已存在於dict中,則insert什麼也不做
    cout << "attention: ret2.first is a iterator points the element we insert" << endl;
    cout << ret2.first->first << ":" << ret2.first->second << endl;
    cout << "key1 insert successfully? " << ret2.second << endl;
}
//========= 執行結果 ==========//
attention: ret1.first is a iterator points the element we insert
key2:1
key2 insert successfully? 1
attention: ret2.first is a iterator points the element we insert
key1:1
key1 insert successfully? 0

例子中的返回值 pair 的完整型別如下:

pair<map<string, int>::iterator, bool>

但是!上面的 insert 方式還是麻煩了點,不如直接使用文章開頭說的下標操作!如果鍵 key 對還未出現在 dict 中,下標運算會建立一個新元素,其值預設為 0 。

當插入一個元素範圍時,可以做如下操作,這時沒有返回值。

dict.insert(dict2.begin(), dict2.end());

5、刪除元素

map 有三個版本的 erase 操作,分別如下:

1、dict.erase(key)

從 dict 中刪除 鍵 為 key 的元素,返回刪除元素的數量,對於 map 型別來說為 1。

2、dict.erase(p)

從 dict 中刪除迭代器 p 指定的元素。p 必須指向 dict 中一個真實元素且不能等於 dict.end()。返回一個指向 p 之後的元素的迭代器。

3、dict.erase(b, e)

從 dict 中刪除迭代器 b 和 e 之間的元素,返回e。

int main(void)
{
    // 定義並初始化一個 map 型別資料 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}, {"key5", 5}};  
    int ret1 = dict.erase("key1");  // 刪除鍵為 "key1" 的元素
    cout << "ret1: " << ret1 << endl;
    auto ret2 = dict.erase(dict.begin());  // 使用迭代器
    cout << "(ret2->first:ret2->second) = " << ret2->first << ":" << ret2->second << endl;
    auto ret3 = dict.erase(dict.begin(), dict.end());  // 清空
    cout << "ret3->first: " << ret3->first << endl;
}
//=========== 執行結果 ============//
ret1: 1
(ret2->first:ret2->second) = key3:3
ret3->first:

6、存取元素

map 提供了一些查詢元素的操作如下:

1、dict.find(key)
// 返回一個迭代器 ,指向第一個鍵為 key 的元素,若 key 不存在於 dict 中,則返回 dict.end()
2、dict.count(key)
// 返回鍵為 key 的元素的數量,對於 map 來說,返回1
3、dict.lower_bound(key)
// 返回一個迭代器,指向第一個鍵不小於 key 的元素
4、dict.upper_bound(key)
// 返回一個迭代器,指向第一個鍵大於 key 的元素
5、dict.equal_range(key)
// 返回一個迭代器pair,表示關鍵字恩於 key 的元素的範圍。如果不存在,則pair的兩個成員均為
//dict.end()

一般來說,2~5的方法多用於 multimap 型別的資料中。如果只是想查詢一個元素是否存在於 dict 中,則使用 find 是最好的選擇。

另外需要注意的是,如果使用下標操作存取元素,如果該元素並不存在於 dict 中,則該操作會建立一個元素,因此,使用什麼樣的存取方式需要根據我們的使用環境進行選擇。

7、其他細節

map 是基於樹的結構的,並且內部元素是有序的,即是經過鍵排序的:如果鍵的型別是數位,則按照數位大小進行排序,如果是 string,則按照字典序進行排序。

總結

到此這篇關於C++中map、字典的基本使用的文章就介紹到這了,更多相關C++ map字典使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com