C++ map容器用法详解
在 C++ 中,可以用 map 容器来存储具有对应关系的数据。
所谓对应关系,指的是可以从一个数据迅速查找到另外一个数据。通常,我们将前面的数据称为键,后面的数据称为值,它们的组合称为键值对(Key-Value Pair),用
例如,<1, "apple">、<2, "banana">、<3, "cherry"> 是 3 个键值对,其中 1、2、3 是键,"apple"、"banana"、"cherry" 是和各个键对应的值。
也就是说,map 是一种存储键值对的容器。map 容器具有以下两个特点:
map 模板类中包含多种构造函数,常用的有以下几个:
下面的 C++ 代码示例演示了如何使用 map 容器的各种构造函数。
默认情况下,map 容器调用 std::less<T> 规则,根据容器内各键值对的键的大小,对所有键值对做升序排序。我们可以手动指定 std::greater<T> 进行降序排序,必要时还可以自定义排序规则。
下面的 C++ 代码示例演示了如何修改 map 容器的排序规则。
表 1 列出了 map 容器提供的常用成员方法以及各自的功能。
所谓对应关系,指的是可以从一个数据迅速查找到另外一个数据。通常,我们将前面的数据称为键,后面的数据称为值,它们的组合称为键值对(Key-Value Pair),用
<K, V>
表示。例如,<1, "apple">、<2, "banana">、<3, "cherry"> 是 3 个键值对,其中 1、2、3 是键,"apple"、"banana"、"cherry" 是和各个键对应的值。
也就是说,map 是一种存储键值对的容器。map 容器具有以下两个特点:
- 它会自动根据键的值对键值对进行排序,并且可以根据给定的键迅速找到对应的值。
- 在 map 容器中,各个键值对的键不能重复,必须是唯一的。
map容器的构建
map 容器本质是一个模板类,定义在<map>
头文件中,并位于 std 命名空间中。map 模板类中包含多种构造函数,常用的有以下几个:
// 创建一个空的 map 容器,可以提供一个自定义的键比较函数(key_compare),所有键值对做升序排序。 explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); // 输入迭代器 first 和 last 指定的范围内的元素创建一个 map 容器 template <class InputIterator> map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); // 拷贝构造函数,创建一个和参数 x 相同的新 map 容器 map (const map& x); // 移动构造函数,“移动”参数 x 的所有元素到新创建的 map 容器。 map (map&& x); // 使用一个初始化列表来创建一个新 map 容器 map (initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
下面的 C++ 代码示例演示了如何使用 map 容器的各种构造函数。
#include <iostream> #include <map> int main() { // 使用默认构造函数创建一个空的 map std::map<std::string, int> map1; map1["apple"] = 1; // 使用范围构造函数创建一个 map std::pair<std::string, int> arr[] = { {"banana", 2}, {"cherry", 3} }; std::map<std::string, int> map2(arr, arr + sizeof(arr) / sizeof(arr[0])); // 使用拷贝构造函数创建一个新的与 map1 相同的 map std::map<std::string, int> map3(map1); // 使用移动构造函数创建一个新的 map std::map<std::string, int> map4(std::move(map2)); // 注意:map2 不能再用 // 使用初始化列表构造函数创建一个新的 map std::map<std::string, int> map5 = { {"pear", 4}, {"mango", 5} }; return 0; }
默认情况下,map 容器调用 std::less<T> 规则,根据容器内各键值对的键的大小,对所有键值对做升序排序。我们可以手动指定 std::greater<T> 进行降序排序,必要时还可以自定义排序规则。
下面的 C++ 代码示例演示了如何修改 map 容器的排序规则。
#include <iostream> #include <map> #include <string> #include <functional> // 对 std::greater<T> 的定义 // 使用 std::less<T>(默认) std::map<std::string, int> map1 = {{"apple", 1}, {"banana", 2}, {"cherry", 3}}; // 使用 std::greater<T> 进行降序排序 std::map<std::string, int, std::greater<std::string>> map2 = {{"apple", 1}, {"banana", 2}, {"cherry", 3}}; // 自定义比较函数 struct CustomCompare { bool operator()(const std::string& a, const std::string& b) const { return a > b; // 降序 } }; // 使用自定义比较函数 std::map<std::string, int, CustomCompare> map3 = {{"apple", 1}, {"banana", 2}, {"cherry", 3}}; int main() { std::cout << "Map1 (std::less<T>, default, ascending):" << std::endl; for (const auto& pair : map1) { std::cout << pair.first << ": " << pair.second << std::endl; } std::cout << "Map2 (std::greater<T>, descending):" << std::endl; for (const auto& pair : map2) { std::cout << pair.first << ": " << pair.second << std::endl; } std::cout << "Map3 (CustomCompare, descending):" << std::endl; for (const auto& pair : map3) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; }运行结果为:
Map1 (std::less<T>, default, ascending):
apple: 1
banana: 2
cherry: 3
Map2 (std::greater<T>, descending):
cherry: 3
banana: 2
apple: 1
Map3 (CustomCompare, descending):
cherry: 3
banana: 2
apple: 1
map容器的使用
map 模板类提供了诸多实用的成员函数,可以满足大部分场景中操作 map 容器的需要。表 1 列出了 map 容器提供的常用成员方法以及各自的功能。
成员方法 | 功能 |
---|---|
begin() | 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
end() | 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
rbegin() | 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
rend() | 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。 |
find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
lower_bound(key) | 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
upper_bound(key) | 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
equal_range(key) | 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。 |
empty() | 若容器为空,则返回 true;否则 false。 |
size() | 返回当前 map 容器中存有键值对的个数。 |
max_size() | 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 |
operator[] | map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。 |
at(key) | 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。 |
insert() | 向 map 容器中插入键值对。 |
erase() | 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。 |
swap() | 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。 |
clear() | 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。 |
emplace() | 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
emplace_hint() | 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。 |
count(key) | 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。 |
下面的 C++ 程序演示了表中部分成员函数的用法:有关表示各个成员函数的语法格式,读者不需要死记硬背,需要时直接去查 C++ 标准库即可,这里不再过多赘述。
#include <iostream> #include <map> #include <string> using namespace std; int main() { // 创建一个空的 map 容器 map<string, int> fruits; // 插入元素 fruits["apple"] = 1; fruits["banana"] = 2; fruits["cherry"] = 3; fruits.insert(make_pair("date", 4)); // 查找元素并打印 auto it = fruits.find("banana"); if (it != fruits.end()) { cout << "Banana found: " << it->second << endl; } else { cout << "Banana not found" << endl; } // 删除元素 fruits.erase("apple"); // 遍历 map for (const auto& pair : fruits) { cout << pair.first << ": " << pair.second << endl; } return 0; }运行结果为:
Banana found: 2
banana: 2
cherry: 3
date: 4