第一章 容器函数列表及讲解
构造
unordered_multimap (构造函数)
- 函数功能 :构造一个新的
unordered_multimap容器,支持默认构造、范围构造、初始化列表构造、拷贝构造、移动构造以及指定 bucket 数量或自定义哈希函数的构造。 - 函数入参 :
- 默认构造:无参数。
- 范围构造:两个迭代器
first和last,表示元素范围。 - 初始化列表构造:
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
- 函数功能:返回指向容器首元素和尾后位置的迭代器。
- 函数入参:无。
- 函数返回值 :正向迭代器(
iterator或const_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的对象。 - 范围:两个迭代器
first和last。 - 初始化列表:
initializer_list<value_type>。 - 带 hint:迭代器
hint和要插入的值。
- 单值:
- 函数返回值 :
- 单值/带 hint:指向新插入元素的
iterator。 - 范围/初始化列表:
void。
- 单值/带 hint:指向新插入元素的
- 使用举例:
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_iterator或const_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. 修改
使用说明 :insert 和 emplace 用于添加元素,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_range 是 multimap 的核心查找函数,返回包含所有同键元素的迭代器区间,便于批量处理重复键。
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。手动调用 rehash 或 reserve 可提前分配足够的桶,避免在批量插入时发生多次自动扩容,从而提升性能。
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_function 和 key_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;
}