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

时间:2022-03-20 03:19:33

前言

在 C++ 中,map 是关联容器的一种,关联容器将值与键关联到一起,并使用键来查找值。这与 python 中的字典类型类似,也是用来存储键-值对(key-value) 形式的数据的,其同样具有遍历、插入、查询、删除等功能,下面将一一介绍。

1、map 类型的声明

若想在 C++ 中使用 map 类型创建字典,则需要包含 map 头文件,如下:

?
1
#include <map>

map 类型的定义如下:

map<KeyType, ValueType> dict;

其中,KeyType 代表键的数据类型,ValueType 代表值的数据类型。KeyType 默认是 const 类型,即键在确认之后为只读数据。另外,在一个 map 数据中,一个键只能出现一次;而在 multimap 类型数据中,一个键可以出现多次,本次主要讨论 map 数据。看下面一个例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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 时,其默认构造函数会对数据成员进行初始化。一个例子如下:

?
1
2
3
4
5
6
7
8
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)直接遍历:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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,只能用来读取迭代器所指向内容的数据,不能修改数据 。一个使用迭代器遍历的例子如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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 中添加元素的方法如下,不过常用的方式就是第一种方式,剩下的仨了解下就行。

?
1
2
3
4
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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 的完整类型如下:

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

但是!上面的 insert 方式还是麻烦了点,不如直接使用文章开头说的下标操作!如果键 key 对还未出现在 dict 中,下标运算会创建一个新元素,其值默认为 0 。

当插入一个元素范围时,可以做如下操作,这时没有返回值。

?
1
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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
2
3
4
5
6
7
8
9
10
11
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字典使用内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/Flag_ing/article/details/120166575