【C++ STL】std::unordered_multimap

第一章 容器函数列表及讲解

构造

unordered_multimap (构造函数)

  • 函数功能 :构造一个新的 unordered_multimap 容器,支持默认构造、范围构造、初始化列表构造、拷贝构造、移动构造以及指定 bucket 数量或自定义哈希函数的构造。
  • 函数入参
    • 默认构造:无参数。
    • 范围构造:两个迭代器 firstlast,表示元素范围。
    • 初始化列表构造:initializer_list<value_type>
    • 拷贝构造:另一个同类型的 unordered_multimap 对象。
    • 移动构造:另一个同类型的右值 unordered_multimap 对象。
    • 指定 bucket_count:size_type count,可选传入哈希函数和键比较函数。
  • 函数返回值:无(构造对象)。
  • 使用举例
cpp 复制代码
std::unordered_multimap<std::string, int> ummap1; // 默认构造
std::vector<std::pair<std::string, int>> vec = {{"apple", 1}};
std::unordered_multimap<std::string, int> ummap2(vec.begin(), vec.end()); // 范围构造
std::unordered_multimap<std::string, int> ummap3 = {{"apple", 1}}; // 初始化列表
std::unordered_multimap<std::string, int> ummap4(ummap3); // 拷贝构造
std::unordered_multimap<std::string, int> ummap5(std::move(ummap4)); // 移动构造
std::unordered_multimap<std::string, int> ummap6(20); // 指定 bucket_count

赋值

operator=

  • 函数功能:将容器内容替换为指定内容,支持拷贝赋值、移动赋值和初始化列表赋值。
  • 函数入参
    • 拷贝赋值:另一个同类型的 unordered_multimap 对象。
    • 移动赋值:另一个同类型的右值 unordered_multimap 对象。
    • 初始化列表赋值:initializer_list<value_type>
  • 函数返回值*this(当前容器的引用)。
  • 使用举例
cpp 复制代码
std::unordered_multimap<std::string, int> ummap1 = {{"apple", 1}};
std::unordered_multimap<std::string, int> ummap2;
ummap2 = ummap1; // 拷贝赋值
ummap2 = std::move(ummap1); // 移动赋值
ummap2 = {{"apple", 1}, {"banana", 2}}; // 初始化列表赋值

迭代器

begin / end

  • 函数功能:返回指向容器首元素和尾后位置的迭代器。
  • 函数入参:无。
  • 函数返回值 :正向迭代器(iteratorconst_iterator)。
  • 使用举例
cpp 复制代码
for (auto it = ummap.begin(); it != ummap.end(); ++it) {
    std::cout << it->first << ":" << it->second << " ";
}

cbegin / cend

  • 函数功能:返回指向容器首元素和尾后位置的常量迭代器,确保元素不被修改。
  • 函数入参:无。
  • 函数返回值 :常量迭代器(const_iterator)。
  • 使用举例
cpp 复制代码
for (auto it = ummap.cbegin(); it != ummap.cend(); ++it) {
    std::cout << it->first << ":" << it->second << " ";
}

容量

empty

  • 函数功能:检查容器是否为空。
  • 函数入参:无。
  • 函数返回值bool,容器为空返回 true,否则返回 false
  • 使用举例
cpp 复制代码
if (ummap.empty()) {
    std::cout << "容器为空" << std::endl;
}

size

  • 函数功能:返回容器中的元素数量。
  • 函数入参:无。
  • 函数返回值size_type,元素个数。
  • 使用举例
cpp 复制代码
std::cout << "元素数量: " << ummap.size() << std::endl;

max_size

  • 函数功能:返回容器由于系统或库实现限制所能容纳的最大元素数量。
  • 函数入参:无。
  • 函数返回值size_type,最大元素数量。
  • 使用举例
cpp 复制代码
std::cout << "最大容量: " << ummap.max_size() << std::endl;

修改

insert

  • 函数功能:向容器中插入元素。支持插入单值、多值(范围)和初始化列表,支持带提示位置(hint)的插入。
  • 函数入参
    • 单值:value_type 或可转换为 value_type 的对象。
    • 范围:两个迭代器 firstlast
    • 初始化列表:initializer_list<value_type>
    • 带 hint:迭代器 hint 和要插入的值。
  • 函数返回值
    • 单值/带 hint:指向新插入元素的 iterator
    • 范围/初始化列表:void
  • 使用举例
cpp 复制代码
auto it = ummap.insert({"apple", 1});
ummap.insert(vec.begin(), vec.end());
ummap.insert({{"apple", 1}, {"banana", 2}});
ummap.insert(ummap.begin(), {"apple", 1});

emplace / emplace_hint

  • 函数功能 :在容器中原位构造并插入新元素,避免不必要的拷贝或移动。emplace_hint 使用提示位置。
  • 函数入参
    • emplace:构造元素的参数包 args...
    • emplace_hint:提示迭代器 hint 和构造元素的参数包 args...
  • 函数返回值 :指向新插入元素的 iterator
  • 使用举例
cpp 复制代码
auto it1 = ummap.emplace("classA", Student("Alice", 95));
auto it2 = ummap.emplace_hint(ummap.begin(), "classA", Student("Bob", 88));

erase

  • 函数功能:移除容器中的元素。支持按迭代器、按范围或按键值移除。
  • 函数入参
    • 迭代器:const_iterator pos
    • 范围:const_iterator first, const_iterator last
    • 键值:const Key& key
  • 函数返回值
    • 迭代器/范围:指向被移除范围后一个元素的 iterator
    • 键值:size_type,被移除的元素个数。
  • 使用举例
cpp 复制代码
ummap.erase(ummap.begin());
ummap.erase(ummap.begin(), std::next(ummap.begin(), 2));
size_t count = ummap.erase("apple");

clear

  • 函数功能:移除容器中的所有元素。
  • 函数入参:无。
  • 函数返回值void
  • 使用举例
cpp 复制代码
ummap.clear();

swap

  • 函数功能:交换两个容器的内容。
  • 函数入参 :另一个同类型的 unordered_multimap 对象。
  • 函数返回值void
  • 使用举例
cpp 复制代码
ummap1.swap(ummap2);

extract (C++17)

  • 函数功能:从容器中剥离(不销毁)指定节点,返回节点句柄。
  • 函数入参
    • 按键值:const Key& key
    • 按迭代器:const_iterator pos
  • 函数返回值node_type(节点句柄)。
  • 使用举例
cpp 复制代码
auto node = ummap.extract("apple");
auto node2 = ummap.extract(ummap.begin());

merge (C++17)

  • 函数功能:将另一个容器中的所有节点转移到当前容器中(如果当前容器允许该键)。
  • 函数入参 :另一个同类型的 unordered_multimap 对象(源容器)。
  • 函数返回值void
  • 使用举例
cpp 复制代码
ummap1.merge(ummap2);

查找

find

  • 函数功能:查找具有指定键的元素。
  • 函数入参const Key& key
  • 函数返回值 :指向找到的第一个匹配元素的 iterator;若未找到,返回 end()
  • 使用举例
cpp 复制代码
auto it = ummap.find("apple");
if (it != ummap.end()) { /* 找到 */ }

count

  • 函数功能:返回具有指定键的元素数量。
  • 函数入参const Key& key
  • 函数返回值size_type,匹配的元素个数。
  • 使用举例
cpp 复制代码
size_t cnt = ummap.count("apple");

contains (C++20)

  • 函数功能:检查容器中是否存在具有指定键的元素。
  • 函数入参const Key& key
  • 函数返回值bool,存在返回 true,否则返回 false
  • 使用举例
cpp 复制代码
if (ummap.contains("apple")) { /* 存在 */ }

equal_range

  • 函数功能:返回包含所有具有指定键的元素的迭代器范围。
  • 函数入参const Key& key
  • 函数返回值std::pair<iterator, iterator>,表示范围的起始和结束。
  • 使用举例
cpp 复制代码
auto range = ummap.equal_range("apple");
for (auto it = range.first; it != range.second; ++it) { /* 遍历 */ }

Bucket

bucket_count

  • 函数功能:返回容器中的桶(bucket)数量。
  • 函数入参:无。
  • 函数返回值size_type,桶的数量。
  • 使用举例
cpp 复制代码
size_t buckets = ummap.bucket_count();

max_bucket_count

  • 函数功能:返回容器支持的最大桶数量。
  • 函数入参:无。
  • 函数返回值size_type,最大桶数量。
  • 使用举例
cpp 复制代码
size_t max_buckets = ummap.max_bucket_count();

bucket_size

  • 函数功能:返回指定桶中的元素数量。
  • 函数入参size_type n(桶的索引)。
  • 函数返回值size_type,该桶中的元素个数。
  • 使用举例
cpp 复制代码
size_t size = ummap.bucket_size(0);

bucket

  • 函数功能:返回指定键所在的桶索引。
  • 函数入参const Key& key
  • 函数返回值size_type,桶的索引。
  • 使用举例
cpp 复制代码
size_t b = ummap.bucket("apple");

begin(n) / end(n)

  • 函数功能:返回指向指定桶首元素和尾后位置的局部迭代器。
  • 函数入参size_type n(桶的索引)。
  • 函数返回值 :局部迭代器(local_iteratorconst_local_iterator)。
  • 使用举例
cpp 复制代码
for (auto it = ummap.begin(0); it != ummap.end(0); ++it) { /* 遍历桶0 */ }

哈希策略

load_factor

  • 函数功能:返回当前负载因子(元素数量除以桶数量)。
  • 函数入参:无。
  • 函数返回值float,当前负载因子。
  • 使用举例
cpp 复制代码
float lf = ummap.load_factor();

max_load_factor

  • 函数功能:获取或设置最大负载因子。当超过此值时,容器会自动增加桶数量(rehash)。
  • 函数入参 :无(获取)或 float z(设置)。
  • 函数返回值float,最大负载因子(获取时)。
  • 使用举例
cpp 复制代码
float mlf = ummap.max_load_factor();
ummap.max_load_factor(2.0f);

rehash

  • 函数功能:更改桶的数量,使桶数量至少为指定值,并重新分配所有元素。
  • 函数入参size_type count(期望的最小桶数量)。
  • 函数返回值void
  • 使用举例
cpp 复制代码
ummap.rehash(50);

reserve

  • 函数功能:预留空间,使容器能容纳指定数量的元素而不触发 rehash。
  • 函数入参size_type count(期望容纳的元素数量)。
  • 函数返回值void
  • 使用举例
cpp 复制代码
ummap.reserve(100);

观察器

hash_function

  • 函数功能:获取容器使用的哈希函数对象。
  • 函数入参:无。
  • 函数返回值 :哈希函数对象(hasher)。
  • 使用举例
cpp 复制代码
auto hasher = ummap.hash_function();
size_t h = hasher("apple");

key_eq

  • 函数功能:获取容器使用的键等价比较函数对象。
  • 函数入参:无。
  • 函数返回值 :键比较函数对象(key_equal)。
  • 使用举例
cpp 复制代码
auto eq = ummap.key_eq();
bool is_eq = eq("apple", "apple");

非成员函数

operator==

  • 函数功能 :比较两个 unordered_multimap 容器是否包含相同的键值对(不考虑顺序)。
  • 函数入参 :两个同类型的 unordered_multimap 对象。
  • 函数返回值bool,相等返回 true
  • 使用举例
cpp 复制代码
if (ummap1 == ummap2) { /* 相等 */ }

std::swap

  • 函数功能 :特化的 std::swap 算法,交换两个容器的内容。
  • 函数入参 :两个同类型的 unordered_multimap 对象。
  • 函数返回值void
  • 使用举例
cpp 复制代码
std::swap(ummap1, ummap2);

std::erase_if (C++20)

  • 函数功能:擦除容器中满足指定谓词的所有元素。
  • 函数入参 :容器对象和谓词函数(Pred)。
  • 函数返回值size_type,被擦除的元素个数。
  • 使用举例
cpp 复制代码
size_t count = std::erase_if(ummap, [](const auto& p) { return p.second % 2 == 0; });

第二章 分模块代码及使用说明

1. 构造与赋值

使用说明unordered_multimap 支持多种构造方式。默认构造生成空容器;范围构造和初始化列表构造可快速填入数据;拷贝和移动构造用于对象复制与转移。赋值操作与构造类似,支持覆盖原有内容。指定 bucket_count 可优化初始哈希表大小,自定义哈希函数允许使用复杂对象作为键。

cpp 复制代码
void defaultConstruct()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ "apple", 1 });
    std::cout << "默认构造后 size: " << ummap.size() << std::endl;
}

void rangeConstruct()
{
    std::vector<std::pair<std::string, int>> vec = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::unordered_multimap<std::string, int> ummap(vec.begin(), vec.end());
    std::cout << "范围构造后 size: " << ummap.size() << std::endl;
}

void initializerListConstruct()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    std::cout << "初始化列表构造后 size: " << ummap.size() << std::endl;
}

void copyConstruct()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2(ummap1);
    std::cout << "拷贝构造后 ummap2 size: " << ummap2.size() << std::endl;
}

void moveConstruct()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2(std::move(ummap1));
    std::cout << "移动构造后 ummap2 size: " << ummap2.size() << std::endl;
    std::cout << "移动构造后 ummap1 size: " << ummap1.size() << std::endl;
}

void bucketCountConstruct()
{
    std::unordered_multimap<std::string, int> ummap(20);
    std::cout << "指定 bucket_count 构造后 bucket_count: " << ummap.bucket_count() << std::endl;
}

void customHashConstruct()
{
    std::unordered_multimap<Course, std::string, CourseHash> ummap(10, CourseHash());
    ummap.insert({ Course("Math", 101), "张老师" });
    std::cout << "自定义哈希构造后 size: " << ummap.size() << std::endl;
}

void copyAssign()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2;
    ummap2 = ummap1;
    std::cout << "拷贝赋值后 ummap2 size: " << ummap2.size() << std::endl;
}

void moveAssign()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2;
    ummap2 = std::move(ummap1);
    std::cout << "移动赋值后 ummap2 size: " << ummap2.size() << std::endl;
}

void initializerListAssign()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "初始化列表赋值后 size: " << ummap.size() << std::endl;
}

2. 迭代器

使用说明 :通过 begin()/end() 获取常规迭代器进行正向遍历;范围 for 循环是更简洁的替代方案。对于 const 容器或需保证元素不被修改的场景,必须使用 cbegin()/cend() 获取常量迭代器。

cpp 复制代码
void iterateForward()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "正向迭代: ";
    for (auto it = ummap.begin(); it != ummap.end(); ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

void iterateRangeFor()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "范围 for 迭代: ";
    for (const auto& pair : ummap)
    {
        std::cout << "[" << pair.first << ":" << pair.second << "] ";
    }
    std::cout << std::endl;
}

void constIteratorDemo()
{
    const std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2} };
    std::cout << "const 迭代: ";
    for (auto it = ummap.cbegin(); it != ummap.cend(); ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

3. 容量

使用说明empty() 用于快速判空,比 size() == 0 更直观。size() 返回当前有效元素总数(包含重复键)。max_size() 反映理论上限,通常由系统内存和分配器决定。

cpp 复制代码
void emptyCheck()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "空容器 empty(): " << ummap.empty() << std::endl;
    ummap.insert({ "apple", 1 });
    std::cout << "插入后 empty(): " << ummap.empty() << std::endl;
}

void sizeCheck()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "size(): " << ummap.size() << std::endl;
}

void maxSizeCheck()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_size(): " << ummap.max_size() << std::endl;
}

4. 修改

使用说明insertemplace 用于添加元素,multimap 允许重复键,因此插入始终成功。erase 支持按迭代器、范围或键名删除(按键删除会移除所有匹配项)。C++17 引入的 extract 可无损剥离节点,merge 可在容器间高效转移节点而不触发内存分配。

cpp 复制代码
void insertSingle()
{
    std::unordered_multimap<std::string, int> ummap;
    auto it = ummap.insert({ "apple", 1 });
    std::cout << "insert 返回迭代器指向: " << it->first << ":" << it->second << std::endl;
}

void insertMultiple()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ "apple", 1 }); ummap.insert({ "apple", 2 }); ummap.insert({ "apple", 3 });
    std::cout << "插入多个重复 key 后 size: " << ummap.size() << std::endl;
}

void insertRange()
{
    std::unordered_multimap<std::string, int> ummap;
    std::vector<std::pair<std::string, int>> vec = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    ummap.insert(vec.begin(), vec.end());
    std::cout << "范围 insert 后 size: " << ummap.size() << std::endl;
}

void insertInitializerList()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ {"apple", 1}, {"banana", 2}, {"apple", 3} });
    std::cout << "初始化列表 insert 后 size: " << ummap.size() << std::endl;
}

void insertHint()
{
    std::unordered_multimap<std::string, int> ummap;
    auto hint = ummap.begin();
    ummap.insert(hint, { "apple", 1 });
    ummap.insert(hint, { "banana", 2 });
    std::cout << "带 hint insert 后 size: " << ummap.size() << std::endl;
}

void emplaceDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    auto it = ummap.emplace("classA", Student("Alice", 95));
    std::cout << "emplace 后: " << it->first << " -> ";
    it->second.display(); std::cout << std::endl;
}

void emplaceHintDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    auto hint = ummap.begin();
    auto it = ummap.emplace_hint(hint, "classA", Student("Bob", 88));
    std::cout << "emplace_hint 后: " << it->first << " -> ";
    it->second.display(); std::cout << std::endl;
}

void eraseByIterator()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    auto it = ummap.begin();
    std::cout << "erase 前: [" << it->first << ":" << it->second << "]" << std::endl;
    ummap.erase(it);
    std::cout << "erase 迭代器后 size: " << ummap.size() << std::endl;
}

void eraseByKey()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    auto count = ummap.erase("apple");
    std::cout << "erase by key 'apple' 删除了 " << count << " 个元素" << std::endl;
    std::cout << "erase 后 size: " << ummap.size() << std::endl;
}

void eraseRange()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    auto start = ummap.begin();
    auto end = std::next(start, 2);
    ummap.erase(start, end);
    std::cout << "erase 范围后 size: " << ummap.size() << std::endl;
}

void clearDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "clear 前 size: " << ummap.size() << std::endl;
    ummap.clear();
    std::cout << "clear 后 size: " << ummap.size() << std::endl;
}

void swapDemo()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1} };
    std::unordered_multimap<std::string, int> ummap2 = { {"banana", 2}, {"cherry", 3} };
    ummap1.swap(ummap2);
    std::cout << "swap 后 ummap1 size: " << ummap1.size() << ", ummap2 size: " << ummap2.size() << std::endl;
}

void extractByKey()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    auto node = ummap.extract("apple");
    if (!node.empty()) { std::cout << "extract by key: " << node.key() << ":" << node.mapped() << std::endl; }
    std::cout << "extract 后 size: " << ummap.size() << std::endl;
}

void extractByIterator()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    auto node = ummap.extract(ummap.begin());
    std::cout << "extract by iterator: " << node.key() << ":" << node.mapped() << std::endl;
    std::cout << "extract 后 size: " << ummap.size() << std::endl;
}

void mergeDemo()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2 = { {"cherry", 3}, {"date", 4} };
    ummap1.merge(ummap2);
    std::cout << "merge 后 ummap1 size: " << ummap1.size() << std::endl;
    std::cout << "merge 后 ummap2 size: " << ummap2.size() << std::endl;
}

5. 查找

使用说明find 返回首个匹配项,count 统计匹配项总数,contains (C++20) 仅判断存在性。equal_rangemultimap 的核心查找函数,返回包含所有同键元素的迭代器区间,便于批量处理重复键。

cpp 复制代码
void findDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    auto it = ummap.find("apple");
    if (it != ummap.end())
    {
        std::cout << "find 'apple': " << it->first << ":" << it->second << std::endl;
    }
    else
    {
        std::cout << "find 'apple': 未找到" << std::endl;
    }
}

void findNotFound()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2} };
    auto it = ummap.find("cherry");
    if (it == ummap.end())
    {
        std::cout << "find 'cherry': 未找到" << std::endl;
    }
}

void countDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"apple", 4} };
    auto cnt = ummap.count("apple");
    std::cout << "count 'apple': " << cnt << std::endl;
}

void containsDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2} };
    std::cout << "contains 'apple': " << ummap.contains("apple") << std::endl;
    std::cout << "contains 'cherry': " << ummap.contains("cherry") << std::endl;
}

void equalRangeDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"apple", 4} };
    auto range = ummap.equal_range("apple");
    std::cout << "equal_range 'apple': ";
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

6. Bucket 接口

使用说明 :哈希表底层由多个桶(bucket)组成。bucket_count 获取桶总数,bucket 定位键所在的桶索引。通过 begin(n)/end(n) 可遍历特定桶内的元素,这在调试哈希冲突或分析数据分布时非常有用。

cpp 复制代码
void bucketCountDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "bucket_count: " << ummap.bucket_count() << std::endl;
}

void maxBucketCountDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_bucket_count: " << ummap.max_bucket_count() << std::endl;
}

void bucketSizeDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    for (std::size_t i = 0; i < ummap.bucket_count(); ++i)
    {
        if (ummap.bucket_size(i) > 0) { std::cout << "bucket " << i << " size: " << ummap.bucket_size(i) << std::endl; }
    }
}

void bucketDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::size_t b = ummap.bucket("apple");
    std::cout << "'apple' 在 bucket: " << b << std::endl;
}

void bucketIteratorDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"cherry", 3} };
    for (std::size_t i = 0; i < ummap.bucket_count(); ++i)
    {
        std::cout << "bucket " << i << ": ";
        for (auto it = ummap.begin(i); it != ummap.end(i); ++it) { std::cout << "[" << it->first << ":" << it->second << "] "; }
        std::cout << std::endl;
    }
}

7. 哈希策略

使用说明load_factor 反映哈希表的拥挤程度。当元素增加导致负载因子超过 max_load_factor 时,容器会自动 rehash。手动调用 rehashreserve 可提前分配足够的桶,避免在批量插入时发生多次自动扩容,从而提升性能。

cpp 复制代码
void loadFactorDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "load_factor: " << ummap.load_factor() << std::endl;
}

void maxLoadFactorDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_load_factor: " << ummap.max_load_factor() << std::endl;
    ummap.max_load_factor(2.0f);
    std::cout << "设置后 max_load_factor: " << ummap.max_load_factor() << std::endl;
}

void rehashDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "rehash 前 bucket_count: " << ummap.bucket_count() << std::endl;
    ummap.rehash(50);
    std::cout << "rehash(50) 后 bucket_count: " << ummap.bucket_count() << std::endl;
}

void reserveDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.reserve(100);
    std::cout << "reserve(100) 后 bucket_count: " << ummap.bucket_count() << std::endl;
}

8. 观察器

使用说明hash_functionkey_eq 分别返回容器内部使用的哈希函数和键比较函数对象。这在需要复用容器哈希逻辑(如计算外部字符串的哈希值)或进行自定义比较时非常实用。

cpp 复制代码
void hashFunctionDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    auto hasher = ummap.hash_function();
    std::cout << "hash('apple'): " << hasher("apple") << std::endl;
    std::cout << "hash('banana'): " << hasher("banana") << std::endl;
}

void keyEqDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    auto eq = ummap.key_eq();
    std::cout << "key_eq 'apple' == 'apple': " << eq("apple", "apple") << std::endl;
    std::cout << "key_eq 'apple' == 'banana': " << eq("apple", "banana") << std::endl;
}

9. 非成员函数

使用说明operator== 用于判断两个容器内容是否完全一致(忽略元素顺序)。std::swap 提供高效的容器内容交换。C++20 的 std::erase_if 允许通过 Lambda 表达式按条件批量擦除元素,简化了传统的"遍历+erase"模式。

cpp 复制代码
void equalityCompare()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap2 = { {"apple", 1}, {"banana", 2} };
    std::unordered_multimap<std::string, int> ummap3 = { {"apple", 1}, {"cherry", 3} };
    std::cout << "ummap1 == ummap2: " << (ummap1 == ummap2) << std::endl;
    std::cout << "ummap1 == ummap3: " << (ummap1 == ummap3) << std::endl;
}

void swapNonMember()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1} };
    std::unordered_multimap<std::string, int> ummap2 = { {"banana", 2}, {"cherry", 3} };
    std::swap(ummap1, ummap2);
    std::cout << "std::swap 后 ummap1 size: " << ummap1.size() << ", ummap2 size: " << ummap2.size() << std::endl;
}

void eraseIfDemo()
{
    std::unordered_multimap<std::string, int> ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4} };
    auto count = std::erase_if(ummap, [](const auto& pair) { return pair.second % 2 == 0; });
    std::cout << "erase_if 删除了 " << count << " 个元素" << std::endl;
    std::cout << "erase_if 后 size: " << ummap.size() << std::endl;
}

10. 自定义类型与实际应用

使用说明 :使用自定义类作为键时,必须提供 operator== 和自定义哈希函数类。unordered_multimap 天然适合"一对多"场景,如按类别分组数据、构建搜索引擎的倒排索引(单词到文档ID的映射),以及表示图的邻接表(节点到相邻节点的映射)。

cpp 复制代码
void customKeyDemo()
{
    std::unordered_multimap<Course, std::string, CourseHash> ummap;
    ummap.insert({ Course("Math", 101), "张老师" });
    ummap.insert({ Course("Math", 101), "李老师" });
    ummap.insert({ Course("English", 202), "王老师" });
    std::cout << "自定义 key size: " << ummap.size() << std::endl;
    auto range = ummap.equal_range(Course("Math", 101));
    std::cout << "Course Math-101 的老师: ";
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

void customValueDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    ummap.insert({ "classA", Student("Alice", 95) });
    ummap.insert({ "classA", Student("Bob", 88) });
    ummap.insert({ "classB", Student("Charlie", 72) });
    std::cout << "自定义 value size: " << ummap.size() << std::endl;
    auto range = ummap.equal_range("classA");
    std::cout << "classA 的学生: ";
    for (auto it = range.first; it != range.second; ++it)
    {
        it->second.display();
        std::cout << " ";
    }
    std::cout << std::endl;
}

void groupByCategory()
{
    std::unordered_multimap<std::string, std::string> ummap = {
        {"fruit", "apple"}, {"vegetable", "carrot"}, {"fruit", "banana"},
        {"fruit", "cherry"}, {"vegetable", "broccoli"}, {"meat", "beef"}
    };
    std::cout << "按类别分组:" << std::endl;
    std::vector<std::string> categories = { "fruit", "vegetable", "meat" };
    for (const auto& cat : categories)
    {
        std::cout << "  " << cat << ": ";
        auto range = ummap.equal_range(cat);
        for (auto it = range.first; it != range.second; ++it)
        {
            std::cout << it->second << " ";
        }
        std::cout << std::endl;
    }
}

void invertIndex()
{
    std::unordered_multimap<std::string, int> wordToDoc;
    wordToDoc.insert({ "hello", 1 }); wordToDoc.insert({ "world", 1 });
    wordToDoc.insert({ "hello", 2 }); wordToDoc.insert({ "foo", 2 });
    wordToDoc.insert({ "hello", 3 }); wordToDoc.insert({ "bar", 3 });
    std::cout << "倒排索引 - 'hello' 出现在文档: ";
    auto range = wordToDoc.equal_range("hello");
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

void adjacencyList()
{
    std::unordered_multimap<std::string, std::string> graph;
    graph.insert({ "A", "B" }); graph.insert({ "A", "C" });
    graph.insert({ "B", "C" }); graph.insert({ "B", "D" }); graph.insert({ "C", "D" });
    std::cout << "邻接表 - 节点 A 的邻居: ";
    auto range = graph.equal_range("A");
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

第三章 完整代码

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>

/*
* 自定义类型
*/

class Student
{
public:
    Student() : name_(""), score_(0) {}
    Student(const std::string& name, int score) : name_(name), score_(score) {}

    std::string getName() const { return name_; }
    int getScore() const { return score_; }
    void display() const
    {
        std::cout << "Student(" << name_ << ", " << score_ << ")";
    }

    bool operator==(const Student& other) const
    {
        return name_ == other.name_ && score_ == other.score_;
    }

private:
    std::string name_;
    int score_;
};

// 自定义类:用作 key 类型
class Course
{
public:
    Course() : courseName_(""), courseCode_(0) {}
    Course(const std::string& courseName, int courseCode)
        : courseName_(courseName), courseCode_(courseCode) {}

    std::string getCourseName() const { return courseName_; }
    int getCourseCode() const { return courseCode_; }

    bool operator==(const Course& other) const
    {
        return courseName_ == other.courseName_ && courseCode_ == other.courseCode_;
    }

private:
    std::string courseName_;
    int courseCode_;
};

// 自定义 key 的哈希函数类
class CourseHash
{
public:
    std::size_t operator()(const Course& c) const
    {
        std::size_t h1 = std::hash<std::string>{}(c.getCourseName());
        std::size_t h2 = std::hash<int>{}(c.getCourseCode());
        return h1 ^ (h2 << 1);
    }
};

// ========================================

/*
* 构造相关
*/

void defaultConstruct()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ "apple", 1 });
    std::cout << "默认构造后 size: " << ummap.size() << std::endl;
}

void rangeConstruct()
{
    std::vector<std::pair<std::string, int>> vec = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::unordered_multimap<std::string, int> ummap(vec.begin(), vec.end());
    std::cout << "范围构造后 size: " << ummap.size() << std::endl;
}

void initializerListConstruct()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    std::cout << "初始化列表构造后 size: " << ummap.size() << std::endl;
}

void copyConstruct()
{
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2(ummap1);
    std::cout << "拷贝构造后 ummap2 size: " << ummap2.size() << std::endl;
}

void moveConstruct()
{
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2(std::move(ummap1));
    std::cout << "移动构造后 ummap2 size: " << ummap2.size() << std::endl;
    std::cout << "移动构造后 ummap1 size: " << ummap1.size() << std::endl;
}

void bucketCountConstruct()
{
    std::unordered_multimap<std::string, int> ummap(20);
    std::cout << "指定 bucket_count 构造后 bucket_count: " << ummap.bucket_count() << std::endl;
}

void customHashConstruct()
{
    std::unordered_multimap<Course, std::string, CourseHash> ummap(
        10, CourseHash());
    ummap.insert({ Course("Math", 101), "张老师" });
    std::cout << "自定义哈希构造后 size: " << ummap.size() << std::endl;
}

// ========================================

/*
* 赋值相关
*/

void copyAssign()
{
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2;
    ummap2 = ummap1;
    std::cout << "拷贝赋值后 ummap2 size: " << ummap2.size() << std::endl;
}

void moveAssign()
{
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2;
    ummap2 = std::move(ummap1);
    std::cout << "移动赋值后 ummap2 size: " << ummap2.size() << std::endl;
}

void initializerListAssign()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap = { {"apple", 1}, {"banana", 2}, {"apple", 3} };
    std::cout << "初始化列表赋值后 size: " << ummap.size() << std::endl;
}

// ========================================

/*
* 迭代器相关
*/

void iterateForward()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "正向迭代: ";
    for (auto it = ummap.begin(); it != ummap.end(); ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

void iterateRangeFor()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "范围 for 迭代: ";
    for (const auto& pair : ummap)
    {
        std::cout << "[" << pair.first << ":" << pair.second << "] ";
    }
    std::cout << std::endl;
}

void constIteratorDemo()
{
    const std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}
    };
    std::cout << "const 迭代: ";
    for (auto it = ummap.cbegin(); it != ummap.cend(); ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

// ========================================

/*
* 容量相关
*/

void emptyCheck()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "空容器 empty(): " << ummap.empty() << std::endl;
    ummap.insert({ "apple", 1 });
    std::cout << "插入后 empty(): " << ummap.empty() << std::endl;
}

void sizeCheck()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "size(): " << ummap.size() << std::endl;
}

void maxSizeCheck()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_size(): " << ummap.max_size() << std::endl;
}

// ========================================

/*
* 修改相关
*/

void insertSingle()
{
    std::unordered_multimap<std::string, int> ummap;
    auto it = ummap.insert({ "apple", 1 });
    std::cout << "insert 返回迭代器指向: "
        << it->first << ":" << it->second << std::endl;
}

void insertMultiple()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ "apple", 1 });
    ummap.insert({ "apple", 2 });
    ummap.insert({ "apple", 3 });
    std::cout << "插入多个重复 key 后 size: " << ummap.size() << std::endl;
}

void insertRange()
{
    std::unordered_multimap<std::string, int> ummap;
    std::vector<std::pair<std::string, int>> vec = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    ummap.insert(vec.begin(), vec.end());
    std::cout << "范围 insert 后 size: " << ummap.size() << std::endl;
}

void insertInitializerList()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.insert({ {"apple", 1}, {"banana", 2}, {"apple", 3} });
    std::cout << "初始化列表 insert 后 size: " << ummap.size() << std::endl;
}

void insertHint()
{
    std::unordered_multimap<std::string, int> ummap;
    auto hint = ummap.begin();
    ummap.insert(hint, { "apple", 1 });
    ummap.insert(hint, { "banana", 2 });
    std::cout << "带 hint insert 后 size: " << ummap.size() << std::endl;
}

void emplaceDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    auto it = ummap.emplace("classA", Student("Alice", 95));
    std::cout << "emplace 后: "
        << it->first << " -> ";
    it->second.display();
    std::cout << std::endl;
}

void emplaceHintDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    auto hint = ummap.begin();
    auto it = ummap.emplace_hint(hint, "classA", Student("Bob", 88));
    std::cout << "emplace_hint 后: "
        << it->first << " -> ";
    it->second.display();
    std::cout << std::endl;
}

void eraseByIterator()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    auto it = ummap.begin();
    std::cout << "erase 前: [" << it->first << ":" << it->second << "]" << std::endl;
    ummap.erase(it);
    std::cout << "erase 迭代器后 size: " << ummap.size() << std::endl;
}

void eraseByKey()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    auto count = ummap.erase("apple");
    std::cout << "erase by key 'apple' 删除了 " << count << " 个元素" << std::endl;
    std::cout << "erase 后 size: " << ummap.size() << std::endl;
}

void eraseRange()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    auto start = ummap.begin();
    auto end = std::next(start, 2);
    ummap.erase(start, end);
    std::cout << "erase 范围后 size: " << ummap.size() << std::endl;
}

void clearDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "clear 前 size: " << ummap.size() << std::endl;
    ummap.clear();
    std::cout << "clear 后 size: " << ummap.size() << std::endl;
}

void swapDemo()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1} };
    std::unordered_multimap<std::string, int> ummap2 = { {"banana", 2}, {"cherry", 3} };
    ummap1.swap(ummap2);
    std::cout << "swap 后 ummap1 size: " << ummap1.size()
        << ", ummap2 size: " << ummap2.size() << std::endl;
}

void extractByKey()
{
#if _MSVC_LANG >= 201703L
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    auto node = ummap.extract("apple");
    if (!node.empty())
    {
        std::cout << "extract by key: "
            << node.key() << ":" << node.mapped() << std::endl;
    }
    std::cout << "extract 后 size: " << ummap.size() << std::endl;
#endif
}

void extractByIterator()
{
#if _MSVC_LANG >= 201703L
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    auto node = ummap.extract(ummap.begin());
    std::cout << "extract by iterator: "
        << node.key() << ":" << node.mapped() << std::endl;
    std::cout << "extract 后 size: " << ummap.size() << std::endl;
#endif
}

void mergeDemo()
{
#if _MSVC_LANG >= 201703L
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2 = {
        {"cherry", 3}, {"date", 4}
    };
    ummap1.merge(ummap2);
    std::cout << "merge 后 ummap1 size: " << ummap1.size() << std::endl;
    std::cout << "merge 后 ummap2 size: " << ummap2.size() << std::endl;
#endif
}

// ========================================

/*
* 查找相关
*/

void findDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    auto it = ummap.find("apple");
    if (it != ummap.end())
    {
        std::cout << "find 'apple': " << it->first << ":" << it->second << std::endl;
    }
    else
    {
        std::cout << "find 'apple': 未找到" << std::endl;
    }
}

void findNotFound()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}
    };
    auto it = ummap.find("cherry");
    if (it == ummap.end())
    {
        std::cout << "find 'cherry': 未找到" << std::endl;
    }
}

void countDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"apple", 4}
    };
    auto cnt = ummap.count("apple");
    std::cout << "count 'apple': " << cnt << std::endl;
}

void containsDemo()
{
#if _MSVC_LANG >= 202002L
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}
    };
    std::cout << "contains 'apple': " << ummap.contains("apple") << std::endl;
    std::cout << "contains 'cherry': " << ummap.contains("cherry") << std::endl;
#endif
}

void equalRangeDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"apple", 4}
    };
    auto range = ummap.equal_range("apple");
    std::cout << "equal_range 'apple': ";
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << "[" << it->first << ":" << it->second << "] ";
    }
    std::cout << std::endl;
}

// ========================================

/*
* Bucket 相关
*/

void bucketCountDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "bucket_count: " << ummap.bucket_count() << std::endl;
}

void maxBucketCountDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_bucket_count: " << ummap.max_bucket_count() << std::endl;
}

void bucketSizeDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    for (std::size_t i = 0; i < ummap.bucket_count(); ++i)
    {
        if (ummap.bucket_size(i) > 0)
        {
            std::cout << "bucket " << i << " size: " << ummap.bucket_size(i) << std::endl;
        }
    }
}

void bucketDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::size_t b = ummap.bucket("apple");
    std::cout << "'apple' 在 bucket: " << b << std::endl;
}

void bucketIteratorDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"cherry", 3}
    };
    for (std::size_t i = 0; i < ummap.bucket_count(); ++i)
    {
        std::cout << "bucket " << i << ": ";
        for (auto it = ummap.begin(i); it != ummap.end(i); ++it)
        {
            std::cout << "[" << it->first << ":" << it->second << "] ";
        }
        std::cout << std::endl;
    }
}

// ========================================

/*
* 哈希策略相关
*/

void loadFactorDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "load_factor: " << ummap.load_factor() << std::endl;
}

void maxLoadFactorDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    std::cout << "max_load_factor: " << ummap.max_load_factor() << std::endl;
    ummap.max_load_factor(2.0f);
    std::cout << "设置后 max_load_factor: " << ummap.max_load_factor() << std::endl;
}

void rehashDemo()
{
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}
    };
    std::cout << "rehash 前 bucket_count: " << ummap.bucket_count() << std::endl;
    ummap.rehash(50);
    std::cout << "rehash(50) 后 bucket_count: " << ummap.bucket_count() << std::endl;
}

void reserveDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    ummap.reserve(100);
    std::cout << "reserve(100) 后 bucket_count: " << ummap.bucket_count() << std::endl;
}

// ========================================

/*
* 观察器相关
*/

void hashFunctionDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    auto hasher = ummap.hash_function();
    std::cout << "hash('apple'): " << hasher("apple") << std::endl;
    std::cout << "hash('banana'): " << hasher("banana") << std::endl;
}

void keyEqDemo()
{
    std::unordered_multimap<std::string, int> ummap;
    auto eq = ummap.key_eq();
    std::cout << "key_eq 'apple' == 'apple': " << eq("apple", "apple") << std::endl;
    std::cout << "key_eq 'apple' == 'banana': " << eq("apple", "banana") << std::endl;
}

// ========================================

/*
* 非成员函数相关
*/

void equalityCompare()
{
    std::unordered_multimap<std::string, int> ummap1 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap2 = {
        {"apple", 1}, {"banana", 2}
    };
    std::unordered_multimap<std::string, int> ummap3 = {
        {"apple", 1}, {"cherry", 3}
    };
    std::cout << "ummap1 == ummap2: " << (ummap1 == ummap2) << std::endl;
    std::cout << "ummap1 == ummap3: " << (ummap1 == ummap3) << std::endl;
}

void swapNonMember()
{
    std::unordered_multimap<std::string, int> ummap1 = { {"apple", 1} };
    std::unordered_multimap<std::string, int> ummap2 = { {"banana", 2}, {"cherry", 3} };
    std::swap(ummap1, ummap2);
    std::cout << "std::swap 后 ummap1 size: " << ummap1.size()
        << ", ummap2 size: " << ummap2.size() << std::endl;
}

void eraseIfDemo()
{
#if _MSVC_LANG >= 202002L
    std::unordered_multimap<std::string, int> ummap = {
        {"apple", 1}, {"banana", 2}, {"apple", 3}, {"cherry", 4}
    };
    auto count = std::erase_if(ummap, [](const auto& pair) {
        return pair.second % 2 == 0;
        });
    std::cout << "erase_if 删除了 " << count << " 个元素" << std::endl;
    std::cout << "erase_if 后 size: " << ummap.size() << std::endl;
#endif
}

// ========================================

/*
* 自定义类型使用
*/

void customKeyDemo()
{
    std::unordered_multimap<Course, std::string, CourseHash> ummap;
    ummap.insert({ Course("Math", 101), "张老师" });
    ummap.insert({ Course("Math", 101), "李老师" });
    ummap.insert({ Course("English", 202), "王老师" });
    std::cout << "自定义 key size: " << ummap.size() << std::endl;
    auto range = ummap.equal_range(Course("Math", 101));
    std::cout << "Course Math-101 的老师: ";
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

void customValueDemo()
{
    std::unordered_multimap<std::string, Student> ummap;
    ummap.insert({ "classA", Student("Alice", 95) });
    ummap.insert({ "classA", Student("Bob", 88) });
    ummap.insert({ "classB", Student("Charlie", 72) });
    std::cout << "自定义 value size: " << ummap.size() << std::endl;
    auto range = ummap.equal_range("classA");
    std::cout << "classA 的学生: ";
    for (auto it = range.first; it != range.second; ++it)
    {
        it->second.display();
        std::cout << " ";
    }
    std::cout << std::endl;
}

// ========================================

/*
* 应用场景
*/

void groupByCategory()
{
    std::unordered_multimap<std::string, std::string> ummap = {
        {"fruit", "apple"}, {"vegetable", "carrot"}, {"fruit", "banana"},
        {"fruit", "cherry"}, {"vegetable", "broccoli"}, {"meat", "beef"}
    };
    std::cout << "按类别分组:" << std::endl;
    std::vector<std::string> categories = { "fruit", "vegetable", "meat" };
    for (const auto& cat : categories)
    {
        std::cout << "  " << cat << ": ";
        auto range = ummap.equal_range(cat);
        for (auto it = range.first; it != range.second; ++it)
        {
            std::cout << it->second << " ";
        }
        std::cout << std::endl;
    }
}

void invertIndex()
{
    std::unordered_multimap<std::string, int> wordToDoc;
    wordToDoc.insert({ "hello", 1 });
    wordToDoc.insert({ "world", 1 });
    wordToDoc.insert({ "hello", 2 });
    wordToDoc.insert({ "foo", 2 });
    wordToDoc.insert({ "hello", 3 });
    wordToDoc.insert({ "bar", 3 });

    std::cout << "倒排索引 - 'hello' 出现在文档: ";
    auto range = wordToDoc.equal_range("hello");
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

void adjacencyList()
{
    std::unordered_multimap<std::string, std::string> graph;
    graph.insert({ "A", "B" });
    graph.insert({ "A", "C" });
    graph.insert({ "B", "C" });
    graph.insert({ "B", "D" });
    graph.insert({ "C", "D" });

    std::cout << "邻接表 - 节点 A 的邻居: ";
    auto range = graph.equal_range("A");
    for (auto it = range.first; it != range.second; ++it)
    {
        std::cout << it->second << " ";
    }
    std::cout << std::endl;
}

// ========================================

int main()
{
    std::cout << "==================== 构造 ====================" << std::endl;
    defaultConstruct();
    rangeConstruct();
    initializerListConstruct();
    copyConstruct();
    moveConstruct();
    bucketCountConstruct();
    customHashConstruct();

    std::cout << "\n==================== 赋值 ====================" << std::endl;
    copyAssign();
    moveAssign();
    initializerListAssign();

    std::cout << "\n==================== 迭代器 ====================" << std::endl;
    iterateForward();
    iterateRangeFor();
    constIteratorDemo();

    std::cout << "\n==================== 容量 ====================" << std::endl;
    emptyCheck();
    sizeCheck();
    maxSizeCheck();

    std::cout << "\n==================== 修改 ====================" << std::endl;
    insertSingle();
    insertMultiple();
    insertRange();
    insertInitializerList();
    insertHint();
    emplaceDemo();
    emplaceHintDemo();
    eraseByIterator();
    eraseByKey();
    eraseRange();
    clearDemo();
    swapDemo();
    extractByKey();
    extractByIterator();
    mergeDemo();

    std::cout << "\n==================== 查找 ====================" << std::endl;
    findDemo();
    findNotFound();
    countDemo();
    containsDemo();
    equalRangeDemo();

    std::cout << "\n==================== Bucket ====================" << std::endl;
    bucketCountDemo();
    maxBucketCountDemo();
    bucketSizeDemo();
    bucketDemo();
    bucketIteratorDemo();

    std::cout << "\n==================== 哈希策略 ====================" << std::endl;
    loadFactorDemo();
    maxLoadFactorDemo();
    rehashDemo();
    reserveDemo();

    std::cout << "\n==================== 观察器 ====================" << std::endl;
    hashFunctionDemo();
    keyEqDemo();

    std::cout << "\n==================== 非成员函数 ====================" << std::endl;
    equalityCompare();
    swapNonMember();
    eraseIfDemo();

    std::cout << "\n==================== 自定义类型 ====================" << std::endl;
    customKeyDemo();
    customValueDemo();

    std::cout << "\n==================== 实际应用 ====================" << std::endl;
    groupByCategory();
    invertIndex();
    adjacencyList();

    return 0;
}
相关推荐
小王C语言2 小时前
【3. 基于 Vibe Coding 的 OJ 平台】. 构建仓库、环境准备、需求梳理、安装依赖
网络·c++
SomeB1oody2 小时前
【RustyML入门】1.0. 快速上手
开发语言·后端·机器学习·rust·教程
狗凯之家源码网2 小时前
短剧系统搭建实战:源码功能与商业变现效果全景展示
开发语言·php
小王C语言2 小时前
【8.进行接口测试】:通过 curl 进行接口自动化测试 / 通过 python 程序进行接口自动化测试
网络·c++
早睡早起身体好1233 小时前
为什么 Network 中会出现两个同名请求?——CORS 预检请求学习笔记
网络·笔记·学习·网络安全
三克的油3 小时前
数据结构-4
数据结构
易筋紫容3 小时前
创建型模式:对象的诞生艺术
开发语言·前端·javascript
888CC++3 小时前
C++ 快速学习指南:从入门到进阶的实战路线
开发语言·c++
小小晓.3 小时前
C++记:函数
开发语言·c++·算法