首页 > 编程笔记

C++ map容器用法详解

在 C++ 中,可以用 map 容器来存储具有对应关系的数据。

所谓对应关系,指的是可以从一个数据迅速查找到另外一个数据。通常,我们将前面的数据称为键,后面的数据称为值,它们的组合称为键值对(Key-Value Pair),用<K, V>表示。

例如,<1, "apple">、<2, "banana">、<3, "cherry"> 是 3 个键值对,其中 1、2、3 是键,"apple"、"banana"、"cherry" 是和各个键对应的值。

也就是说,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 容器提供的常用成员方法以及各自的功能。

表 1 C++ 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

推荐阅读