第一章 std::unordered_multiset 函数列表与讲解
构造函数
默认构造函数
- 函数功能:构造一个空的无序多重集合容器。
- 函数入参:无。
- 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<int> ums;
指定桶数量构造函数
- 函数功能:构造一个空的无序多重集合,并指定初始桶的最小数量。
- 函数入参 :
bucket_count(指定最小桶数量)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<int> ums(20);
自定义哈希和比较函数构造函数
- 函数功能:构造容器并指定自定义的哈希函数对象和相等比较函数对象。
- 函数入参 :
bucket_count(桶数量)、hash(哈希函数对象)、equal(比较函数对象)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<Person, PersonHash, PersonEqual> ums(10, PersonHash(), PersonEqual());
迭代器范围构造函数
- 函数功能:使用迭代器指定范围内的元素来初始化容器。
- 函数入参 :
first(起始迭代器)、last(结束迭代器)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::vector<int> vec = {1, 2, 3, 2};
std::unordered_multiset<int> ums(vec.begin(), vec.end());
初始化列表构造函数
- 函数功能:使用初始化列表中的元素来初始化容器。
- 函数入参 :
init(std::initializer_list初始化列表)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<std::string> ums = {"apple", "banana", "apple"};
拷贝构造函数
- 函数功能:通过复制另一个同类型容器的所有元素和属性来构造新容器。
- 函数入参 :
other(被复制的源容器)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<int> ums2(ums1);
移动构造函数
- 函数功能:通过转移另一个同类型容器的资源来构造新容器,源容器将被置空。
- 函数入参 :
other(被移动的源容器右值引用)。 - 函数返回值:无(构造对象本身)。
- 使用举例:
cpp
std::unordered_multiset<int> ums2(std::move(ums1));
赋值运算符
拷贝赋值运算符
- 函数功能:将另一个容器的内容复制到当前容器,替换原有内容。
- 函数入参 :
other(被复制的源容器)。 - 函数返回值 :当前容器的引用(
*this)。 - 使用举例:
cpp
ums2 = ums1;
移动赋值运算符
- 函数功能:将另一个容器的资源转移到当前容器,源容器被置空。
- 函数入参 :
other(被移动的源容器右值引用)。 - 函数返回值 :当前容器的引用(
*this)。 - 使用举例:
cpp
ums2 = std::move(ums1);
初始化列表赋值运算符
- 函数功能:使用初始化列表中的元素替换当前容器的所有内容。
- 函数入参 :
ilist(std::initializer_list初始化列表)。 - 函数返回值 :当前容器的引用(
*this)。 - 使用举例:
cpp
ums = {10, 20, 30, 20};
迭代器
begin / end
- 函数功能:获取指向容器首元素和尾后位置的迭代器,支持读写操作。
- 函数入参:无。
- 函数返回值 :正向迭代器(
iterator)。 - 使用举例:
cpp
for (auto it = ums.begin(); it != ums.end(); ++it) { /* 访问 *it */ }
cbegin / cend
- 函数功能:获取指向容器首元素和尾后位置的常量迭代器,仅支持只读操作。
- 函数入参:无。
- 函数返回值 :常量正向迭代器(
const_iterator)。 - 使用举例:
cpp
for (auto it = ums.cbegin(); it != ums.cend(); ++it) { /* 只读访问 *it */ }
容量相关
empty
- 函数功能:检查容器是否为空(即不包含任何元素)。
- 函数入参:无。
- 函数返回值 :布尔值(
bool),空则返回true。 - 使用举例:
cpp
bool is_empty = ums.empty();
size
- 函数功能:获取容器中当前存储的元素总数(包含重复元素)。
- 函数入参:无。
- 函数返回值 :元素数量(
size_type)。 - 使用举例:
cpp
std::size_t count = ums.size();
max_size
- 函数功能:获取由于系统或库实现限制,容器所能容纳的最大元素数量。
- 函数入参:无。
- 函数返回值 :最大元素数量(
size_type)。 - 使用举例:
cpp
std::size_t max = ums.max_size();
修改操作
clear
- 函数功能:移除容器中的所有元素,使其变为空容器。
- 函数入参:无。
- 函数返回值:无。
- 使用举例:
cpp
ums.clear();
insert (单值插入)
- 函数功能:向容器中插入单个元素。
- 函数入参 :
value(要插入的元素值)。 - 函数返回值 :指向新插入元素的迭代器(
iterator)。 - 使用举例:
cpp
auto result = ums.insert(42);
insert (提示位置插入)
- 函数功能:使用提示迭代器作为插入位置的参考来插入元素。
- 函数入参 :
hint(提示位置的迭代器)、value(要插入的元素值)。 - 函数返回值 :指向新插入元素的迭代器(
iterator)。 - 使用举例:
cpp
ums.insert(ums.begin(), 99);
insert (范围插入)
- 函数功能:将迭代器指定范围内的所有元素插入到容器中。
- 函数入参 :
first(起始迭代器)、last(结束迭代器)。 - 函数返回值:无。
- 使用举例:
cpp
ums.insert(vec.begin(), vec.end());
insert (初始化列表插入)
- 函数功能:将初始化列表中的所有元素插入到容器中。
- 函数入参 :
ilist(std::initializer_list初始化列表)。 - 函数返回值:无。
- 使用举例:
cpp
ums.insert({"red", "green", "red"});
emplace
- 函数功能:在容器中就地构造并插入一个新元素,避免不必要的拷贝。
- 函数入参 :
args...(转发给元素构造函数的参数包)。 - 函数返回值 :指向新插入元素的迭代器(
iterator)。 - 使用举例:
cpp
auto result = ums.emplace("hello");
emplace_hint
- 函数功能:使用提示位置在容器中就地构造并插入新元素。
- 函数入参 :
hint(提示迭代器)、args...(构造参数包)。 - 函数返回值 :指向新插入元素的迭代器(
iterator)。 - 使用举例:
cpp
ums.emplace_hint(ums.begin(), "hello");
erase (按迭代器删除)
- 函数功能:移除指定迭代器指向的单个元素。
- 函数入参 :
pos(指向要移除元素的迭代器)。 - 函数返回值:指向被移除元素之后那个元素的迭代器。
- 使用举例:
cpp
ums.erase(ums.find(3));
erase (按值删除)
- 函数功能:移除容器中所有与指定值相等的元素。
- 函数入参 :
value(要移除的元素值)。 - 函数返回值 :被移除的元素个数(
size_type)。 - 使用举例:
cpp
std::size_t removed = ums.erase(2);
erase (按范围删除)
- 函数功能 :移除迭代器指定范围
[first, last)内的所有元素。 - 函数入参 :
first(起始迭代器)、last(结束迭代器)。 - 函数返回值 :指向
last的迭代器。 - 使用举例:
cpp
ums.erase(ums.begin(), std::next(ums.begin(), 3));
swap
- 函数功能:交换当前容器与另一个同类型容器的所有内容。
- 函数入参 :
other(要交换的目标容器)。 - 函数返回值:无。
- 使用举例:
cpp
ums1.swap(ums2);
extract (按迭代器提取) (C++17)
- 函数功能:将指定迭代器指向的节点从容器中剥离并返回节点句柄,不触发元素析构。
- 函数入参 :
pos(指向要提取元素的迭代器)。 - 函数返回值 :节点句柄(
node_type)。 - 使用举例:
cpp
auto node = ums.extract(ums.find(3));
extract (按值提取) (C++17)
- 函数功能:搜索并剥离与指定值匹配的一个节点。
- 函数入参 :
key(要提取的元素值)。 - 函数返回值 :节点句柄(
node_type),若未找到则为空句柄。 - 使用举例:
cpp
auto node = ums.extract(2);
merge (C++17)
- 函数功能:将源容器中的所有节点转移到当前容器中(由于是 multiset,所有节点均会被成功转移)。
- 函数入参 :
source(提供节点的源容器)。 - 函数返回值:无。
- 使用举例:
cpp
ums1.merge(ums2);
查找操作
count
- 函数功能:查找容器中与指定键值相等的元素数量。
- 函数入参 :
value(要搜索的元素值)。 - 函数返回值 :匹配的元素个数(
size_type)。 - 使用举例:
cpp
std::size_t c = ums.count(1);
find
- 函数功能:查找容器中与指定键值相等的任意一个元素。
- 函数入参 :
value(要搜索的元素值)。 - 函数返回值 :指向找到元素的迭代器;若未找到则返回
end()。 - 使用举例:
cpp
auto it = ums.find(20);
contains (C++20)
- 函数功能:检查容器中是否包含与指定键值相等的元素。
- 函数入参 :
value(要搜索的元素值)。 - 函数返回值 :布尔值(
bool),包含返回true。 - 使用举例:
cpp
bool has = ums.contains("apple");
equal_range
- 函数功能:获取容器中所有与指定键值相等的元素范围。
- 函数入参 :
value(要搜索的元素值)。 - 函数返回值 :包含一对迭代器的
std::pair,分别指向范围的起始和尾后位置。 - 使用举例:
cpp
auto range = ums.equal_range(2);
桶接口
bucket_count
- 函数功能:获取容器当前拥有的桶(bucket)总数。
- 函数入参:无。
- 函数返回值 :桶的数量(
size_type)。 - 使用举例:
cpp
std::size_t buckets = ums.bucket_count();
max_bucket_count
- 函数功能:获取容器支持的最大桶数量。
- 函数入参:无。
- 函数返回值 :最大桶数量(
size_type)。 - 使用举例:
cpp
std::size_t max_b = ums.max_bucket_count();
bucket_size
- 函数功能:获取指定索引的桶中包含的元素数量。
- 函数入参 :
n(桶的索引)。 - 函数返回值 :该桶中的元素个数(
size_type)。 - 使用举例:
cpp
std::size_t sz = ums.bucket_size(0);
bucket
- 函数功能:获取指定键值被映射到的桶的索引。
- 函数入参 :
key(要查询的键值)。 - 函数返回值 :桶的索引(
size_type)。 - 使用举例:
cpp
std::size_t idx = ums.bucket(10);
begin(n) / end(n)
- 函数功能:获取指定索引桶的起始和尾后迭代器。
- 函数入参 :
n(桶的索引)。 - 函数返回值 :局部迭代器(
local_iterator)。 - 使用举例:
cpp
for (auto it = ums.begin(i); it != ums.end(i); ++it) { /* 遍历桶 i */ }
cbegin(n) / cend(n)
- 函数功能:获取指定索引桶的起始和尾后常量迭代器。
- 函数入参 :
n(桶的索引)。 - 函数返回值 :常量局部迭代器(
const_local_iterator)。 - 使用举例:
cpp
for (auto it = ums.cbegin(i); it != ums.cend(i); ++it) { /* 只读遍历桶 i */ }
哈希策略
load_factor
- 函数功能:获取容器当前的负载因子(元素总数除以桶总数)。
- 函数入参:无。
- 函数返回值 :当前负载因子(
float)。 - 使用举例:
cpp
float lf = ums.load_factor();
max_load_factor (获取)
- 函数功能:获取容器允许的最大负载因子。
- 函数入参:无。
- 函数返回值 :最大负载因子(
float)。 - 使用举例:
cpp
float mlf = ums.max_load_factor();
max_load_factor (设置)
- 函数功能:设置容器允许的最大负载因子,可能会触发 rehash。
- 函数入参 :
mlf(新的最大负载因子)。 - 函数返回值:无。
- 使用举例:
cpp
ums.max_load_factor(2.0f);
rehash
- 函数功能:更改桶的数量,使其至少能容纳指定数量的元素且满足负载因子限制,并重新哈希所有元素。
- 函数入参 :
count(期望的最小桶数量)。 - 函数返回值:无。
- 使用举例:
cpp
ums.rehash(50);
reserve
- 函数功能:预留足够的桶空间,使得容器在不触发 rehash 的情况下能容纳指定数量的元素。
- 函数入参 :
count(期望容纳的元素数量)。 - 函数返回值:无。
- 使用举例:
cpp
ums.reserve(100);
观察器
hash_function
- 函数功能:获取容器当前使用的哈希函数对象。
- 函数入参:无。
- 函数返回值 :哈希函数对象(
hasher)。 - 使用举例:
cpp
auto hasher = ums.hash_function();
std::size_t h = hasher(42);
key_eq
- 函数功能:获取容器当前使用的键值相等比较函数对象。
- 函数入参:无。
- 函数返回值 :键值比较函数对象(
key_equal)。 - 使用举例:
cpp
auto eq = ums.key_eq();
bool is_eq = eq(1, 1);
非成员函数
operator== / operator!=
- 函数功能:比较两个无序多重集合的内容是否相等或不相等(忽略元素顺序)。
- 函数入参 :
lhs(左操作数容器)、rhs(右操作数容器)。 - 函数返回值 :布尔值(
bool)。 - 使用举例:
cpp
bool eq = (ums1 == ums2);
bool neq = (ums1 != ums2);
std::swap
- 函数功能 :
std::swap算法针对unordered_multiset的特化版本,用于交换两个容器的内容。 - 函数入参 :
lhs、rhs(要交换的两个容器)。 - 函数返回值:无。
- 使用举例:
cpp
std::swap(ums1, ums2);
第二章 模块代码与使用说明
1. 构造函数
使用说明:支持多种初始化方式。默认构造创建空容器;可指定初始桶数量优化性能;支持传入自定义哈希和比较器以存储自定义类型;可通过迭代器范围、初始化列表快速填充数据;支持拷贝和移动语义,移动构造后源容器被清空。
cpp
// 1.1 默认构造
void defaultConstructor()
{
std::unordered_multiset<int> ums;
std::cout << "[默认构造] size = " << ums.size() << std::endl;
}
// 1.2 指定桶数量构造
void bucketCountConstructor()
{
std::unordered_multiset<int> ums(20);
std::cout << "[指定桶数量构造] bucket_count = " << ums.bucket_count() << std::endl;
}
// 1.3 带自定义哈希和比较函数构造
void customHashConstructor()
{
std::unordered_multiset<Person, PersonHash, PersonEqual> ums(10, PersonHash(), PersonEqual());
ums.insert(Person("Alice", 25));
std::cout << "[自定义哈希构造] size = " << ums.size() << std::endl;
}
// 1.4 迭代器范围构造
void rangeConstructor()
{
std::vector<int> vec = { 1, 2, 3, 2, 1, 4 };
std::unordered_multiset<int> ums(vec.begin(), vec.end());
std::cout << "[迭代器范围构造] size = " << ums.size() << ", count(1) = " << ums.count(1) << std::endl;
}
// 1.5 初始化列表构造
void initializerListConstructor()
{
std::unordered_multiset<std::string> ums = { "apple", "banana", "apple", "cherry" };
std::cout << "[初始化列表构造] size = " << ums.size() << ", count(apple) = " << ums.count("apple") << std::endl;
}
// 1.6 拷贝构造
void copyConstructor()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3, 2, 1 };
std::unordered_multiset<int> ums2(ums1);
std::cout << "[拷贝构造] ums2.size() = " << ums2.size() << std::endl;
}
// 1.7 移动构造
void moveConstructor()
{
std::unordered_multiset<int> ums1 = { 10, 20, 30, 20 };
std::unordered_multiset<int> ums2(std::move(ums1));
std::cout << "[移动构造] ums2.size() = " << ums2.size()
<< ", ums1.size() = " << ums1.size() << std::endl;
}
2. 赋值运算符
使用说明 :赋值操作会清除容器原有内容。拷贝赋值进行深拷贝,移动赋值转移底层资源(效率更高),初始化列表赋值允许直接使用 {} 语法重新填充容器。
cpp
// 2.1 拷贝赋值
void copyAssignment()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2;
ums2 = ums1;
std::cout << "[拷贝赋值] ums2.size() = " << ums2.size() << std::endl;
}
// 2.2 移动赋值
void moveAssignment()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2;
ums2 = std::move(ums1);
std::cout << "[移动赋值] ums2.size() = " << ums2.size() << std::endl;
}
// 2.3 初始化列表赋值
void initializerListAssignment()
{
std::unordered_multiset<int> ums;
ums = { 10, 20, 30, 20, 10 };
std::cout << "[初始化列表赋值] ums.size() = " << ums.size() << std::endl;
}
3. 迭代器
使用说明 :提供正向迭代器用于遍历容器。由于底层基于哈希表,遍历输出的元素顺序是无序 的。cbegin/cend 返回常量迭代器,保证元素在遍历过程中不被意外修改。
cpp
// 3.1 begin / end 遍历
void beginEnd()
{
std::unordered_multiset<int> ums = { 5, 3, 1, 4, 2, 3 };
std::cout << "[begin/end] 元素: ";
for (auto it = ums.begin(); it != ums.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
// 3.2 cbegin / cend 常量遍历
void cbeginCend()
{
std::unordered_multiset<std::string> ums = { "hello", "world", "hello" };
std::cout << "[cbegin/cend] 元素: ";
for (auto it = ums.cbegin(); it != ums.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
4. 容量相关
使用说明 :empty() 判断容器是否为空,时间复杂度 O(1);size() 返回包含重复元素在内的总元素个数;max_size() 返回系统理论支持的最大容量。
cpp
// 4.1 empty
void checkEmpty()
{
std::unordered_multiset<int> ums1;
std::unordered_multiset<int> ums2 = { 1, 2 };
std::cout << "[empty] ums1.empty() = " << std::boolalpha << ums1.empty()
<< ", ums2.empty() = " << ums2.empty() << std::endl;
}
// 4.2 size
void getSize()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4 };
std::cout << "[size] ums.size() = " << ums.size() << std::endl;
}
// 4.3 max_size
void getMaxSize()
{
std::unordered_multiset<int> ums;
std::cout << "[max_size] ums.max_size() = " << ums.max_size() << std::endl;
}
5. 修改操作
使用说明:
- 插入 :
insert支持单值、范围、列表插入;emplace支持原地构造,性能更优。multiset 允许重复插入相同值。 - 删除 :按迭代器或范围删除仅移除特定位置的元素;按值删除会移除所有等于该值的元素,并返回移除个数。
- 节点操作 (C++17) :
extract将元素从容器中剥离为节点句柄,避免内存重新分配;merge将源容器的所有节点无损转移至目标容器。
cpp
// 5.1 clear
void clearAll()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[clear] 清空前 size = " << ums.size();
ums.clear();
std::cout << ", 清空后 size = " << ums.size() << std::endl;
}
// 5.2 insert - 单值插入
void insertSingle()
{
std::unordered_multiset<int> ums;
auto result = ums.insert(42);
std::cout << "[insert单值] 插入42, *result = " << *result << ", size = " << ums.size() << std::endl;
}
// 5.3 insert - 提示位置插入
void insertWithHint()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto it = ums.begin();
ums.insert(it, 99);
std::cout << "[insert提示] 插入99, size = " << ums.size() << std::endl;
}
// 5.4 insert - 范围插入
void insertRange()
{
std::unordered_multiset<int> ums;
std::vector<int> vec = { 10, 20, 30, 20, 10 };
ums.insert(vec.begin(), vec.end());
std::cout << "[insert范围] size = " << ums.size() << ", count(10) = " << ums.count(10) << std::endl;
}
// 5.5 insert - 初始化列表插入
void insertInitList()
{
std::unordered_multiset<std::string> ums;
ums.insert({ "red", "green", "blue", "red" });
std::cout << "[insert初始化列表] size = " << ums.size() << ", count(red) = " << ums.count("red") << std::endl;
}
// 5.6 emplace
void emplaceElement()
{
std::unordered_multiset<std::string> ums;
auto result = ums.emplace("hello");
std::cout << "[emplace] 构造并插入\"hello\", *result = " << *result
<< ", size = " << ums.size() << std::endl;
}
// 5.7 emplace_hint
void emplaceWithHint()
{
std::unordered_multiset<std::string> ums = { "world" };
auto it = ums.begin();
ums.emplace_hint(it, "hello");
std::cout << "[emplace_hint] size = " << ums.size() << std::endl;
}
// 5.8 erase - 按迭代器删除
void eraseByIterator()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
auto it = ums.find(3);
if (it != ums.end())
{
ums.erase(it);
}
std::cout << "[erase迭代器] 删除3后 size = " << ums.size() << std::endl;
}
// 5.9 erase - 按值删除(删除所有匹配元素)
void eraseByValue()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4 };
auto removed = ums.erase(2);
std::cout << "[erase值] 删除所有2, 移除个数 = " << removed << ", size = " << ums.size() << std::endl;
}
// 5.10 erase - 按范围删除
void eraseByRange()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5, 6 };
auto first = ums.begin();
auto last = ums.begin();
std::advance(last, 3);
ums.erase(first, last);
std::cout << "[erase范围] 删除前3个后 size = " << ums.size() << std::endl;
}
// 5.11 swap
void swapContainers()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 10, 20 };
ums1.swap(ums2);
std::cout << "[swap] ums1.size() = " << ums1.size() << ", ums2.size() = " << ums2.size() << std::endl;
}
// 5.12 extract - 按迭代器提取节点 (C++17)
void extractByIterator()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
auto it = ums.find(3);
if (it != ums.end())
{
auto node = ums.extract(it);
std::cout << "[extract迭代器] 提取节点值 = " << node.value()
<< ", 容器size = " << ums.size() << std::endl;
}
#endif
}
// 5.13 extract - 按值提取节点 (C++17)
void extractByKey()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1 };
auto node = ums.extract(2);
if (!node.empty())
{
std::cout << "[extract值] 提取节点值 = " << node.value()
<< ", 容器size = " << ums.size() << std::endl;
}
#endif
}
// 5.14 merge (C++17)
void mergeContainers()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 4, 5, 6, 5 };
ums1.merge(ums2);
std::cout << "[merge] ums1.size() = " << ums1.size()
<< ", ums2.size() = " << ums2.size() << std::endl;
#endif
}
6. 查找操作
使用说明 :count 返回指定值的出现次数,这是 multiset 的核心特性。find 仅返回指向第一个匹配元素的迭代器。contains (C++20) 提供比 count > 0 更语义化且高效的布尔判断。equal_range 返回一对迭代器,界定出所有等于指定值的元素范围,便于遍历所有重复项。
cpp
// 6.1 count
void countElements()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4, 1 };
std::cout << "[count] count(1) = " << ums.count(1)
<< ", count(2) = " << ums.count(2)
<< ", count(99) = " << ums.count(99) << std::endl;
}
// 6.2 find
void findElement()
{
std::unordered_multiset<int> ums = { 10, 20, 30, 20 };
auto it = ums.find(20);
if (it != ums.end())
{
std::cout << "[find] 找到元素: " << *it << std::endl;
}
auto it2 = ums.find(99);
if (it2 == ums.end())
{
std::cout << "[find] 未找到99" << std::endl;
}
}
// 6.3 contains (C++20)
void containsElement()
{
#if _MSVC_LANG >= 202002L
std::unordered_multiset<std::string> ums = { "apple", "banana", "apple" };
std::cout << "[contains] contains(\"apple\") = " << std::boolalpha << ums.contains("apple")
<< ", contains(\"grape\") = " << ums.contains("grape") << std::endl;
#endif
}
// 6.4 equal_range
void equalRangeElements()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4, 2 };
auto range = ums.equal_range(2);
std::cout << "[equal_range] 元素2的所有出现: ";
for (auto it = range.first; it != range.second; ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
7. 桶接口
使用说明 :桶接口暴露了哈希表的底层结构。可查询总桶数、最大桶数、单个桶的元素个数以及特定元素所在的桶索引。通过局部迭代器 begin(n) 可以单独遍历某个特定桶内的元素,常用于调试哈希冲突情况。
cpp
// 7.1 bucket_count
void getBucketCount()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[bucket_count] bucket_count = " << ums.bucket_count() << std::endl;
}
// 7.2 max_bucket_count
void getMaxBucketCount()
{
std::unordered_multiset<int> ums;
std::cout << "[max_bucket_count] max_bucket_count = " << ums.max_bucket_count() << std::endl;
}
// 7.3 bucket_size
void getBucketSize()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[bucket_size] 桶" << i << " 有 " << ums.bucket_size(i) << " 个元素" << std::endl;
}
}
}
// 7.4 bucket - 获取元素所在桶编号
void getBucketIndex()
{
std::unordered_multiset<int> ums = { 10, 20, 30 };
std::cout << "[bucket] 元素10在桶 " << ums.bucket(10)
<< ", 元素20在桶 " << ums.bucket(20) << std::endl;
}
// 7.5 begin(n) / end(n) - 桶迭代器
void bucketIterators()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[桶迭代器] 桶" << i << ": ";
for (auto it = ums.begin(i); it != ums.end(i); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
}
}
// 7.6 cbegin(n) / cend(n) - 桶常量迭代器
void bucketConstIterators()
{
std::unordered_multiset<int> ums = { 11, 22, 33, 44 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[桶常量迭代器] 桶" << i << ": ";
for (auto it = ums.cbegin(i); it != ums.cend(i); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
}
}
8. 哈希策略
使用说明 :用于控制哈希表的扩容行为。load_factor 是当前元素数与桶数的比值;当它超过 max_load_factor 时会自动触发扩容。rehash 强制重新分配桶并重新哈希;reserve 则根据预期元素数量智能计算并预分配足够的桶,避免插入时频繁扩容。
cpp
// 8.1 load_factor
void getLoadFactor()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[load_factor] load_factor = " << ums.load_factor()
<< ", bucket_count = " << ums.bucket_count() << std::endl;
}
// 8.2 max_load_factor(获取)
void getMaxLoadFactor()
{
std::unordered_multiset<int> ums;
std::cout << "[max_load_factor获取] max_load_factor = " << ums.max_load_factor() << std::endl;
}
// 8.3 max_load_factor(设置)
void setMaxLoadFactor()
{
std::unordered_multiset<int> ums;
ums.max_load_factor(2.0f);
std::cout << "[max_load_factor设置] 设置后 max_load_factor = " << ums.max_load_factor() << std::endl;
}
// 8.4 rehash
void rehashBuckets()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[rehash] 之前 bucket_count = " << ums.bucket_count();
ums.rehash(50);
std::cout << ", 之后 bucket_count = " << ums.bucket_count() << std::endl;
}
// 8.5 reserve
void reserveCapacity()
{
std::unordered_multiset<int> ums;
ums.reserve(100);
std::cout << "[reserve] reserve(100)后 bucket_count = " << ums.bucket_count() << std::endl;
}
9. 观察器
使用说明:允许获取容器实例化时使用的哈希函数对象和键值比较函数对象。可用于在容器外部手动计算哈希值或执行与容器内部一致的相等性判断。
cpp
// 9.1 hash_function
void getHashFunction()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto hasher = ums.hash_function();
std::cout << "[hash_function] hash(42) = " << hasher(42) << std::endl;
}
// 9.2 key_eq
void getKeyEq()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto keyEqual = ums.key_eq();
std::cout << "[key_eq] key_eq(1, 1) = " << std::boolalpha << keyEqual(1, 1)
<< ", key_eq(1, 2) = " << keyEqual(1, 2) << std::endl;
}
10. 非成员函数
使用说明 :提供全局的比较运算符和交换函数。比较操作会检查两个容器是否包含完全相同的元素及其重复次数(忽略存储顺序)。std::swap 提供常数时间复杂度的高效资源交换。
cpp
// 10.1 operator==
void operatorEqual()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 1, 2, 3 };
std::unordered_multiset<int> ums3 = { 1, 2, 4 };
std::cout << "[operator==] ums1 == ums2: " << std::boolalpha << (ums1 == ums2)
<< ", ums1 == ums3: " << (ums1 == ums3) << std::endl;
}
// 10.2 operator!=
void operatorNotEqual()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 4, 5, 6 };
std::cout << "[operator!=] ums1 != ums2: " << std::boolalpha << (ums1 != ums2) << std::endl;
}
// 10.3 std::swap (非成员)
void stdSwapContainers()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 10, 20 };
std::swap(ums1, ums2);
std::cout << "[std::swap] ums1.size() = " << ums1.size()
<< ", ums2.size() = " << ums2.size() << std::endl;
}
11. 自定义类型综合使用
使用说明 :存储自定义类型(如 Person)时,必须在模板参数中提供自定义的哈希函数对象(PersonHash)和相等比较函数对象(PersonEqual)。配置完成后,即可像内置类型一样正常使用 count、equal_range 等所有查找和统计功能。
cpp
void customTypeUsage()
{
std::unordered_multiset<Person, PersonHash, PersonEqual> ums;
ums.insert(Person("Alice", 25));
ums.insert(Person("Bob", 30));
ums.insert(Person("Alice", 25)); // 允许重复
ums.insert(Person("Charlie", 35));
std::cout << "[自定义类型] 所有元素: ";
for (const auto& p : ums)
{
std::cout << p << " ";
}
std::cout << std::endl;
Person target("Alice", 25);
std::cout << "[自定义类型] count(Alice,25) = " << ums.count(target) << std::endl;
auto range = ums.equal_range(target);
std::cout << "[自定义类型] equal_range(Alice,25) 元素: ";
for (auto it = range.first; it != range.second; ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
12. 综合应用场景
使用说明:
- 频率统计 :利用 multiset 自动存储重复元素的特性,结合
count()方法,可极其简便地实现数据频率统计。 - 元素迁移 :在遍历容器时,利用
erase返回下一个有效迭代器的特性,可以安全地在循环中边删除边插入,实现元素在不同容器间的条件迁移。
cpp
// 12.1 统计元素频率
void frequencyCounting()
{
std::vector<int> data = { 1, 2, 3, 2, 1, 4, 3, 2, 1, 5 };
std::unordered_multiset<int> ums(data.begin(), data.end());
std::cout << "[频率统计] 各元素出现次数:" << std::endl;
std::unordered_multiset<int> counted;
for (const auto& val : data)
{
if (counted.find(val) == counted.end())
{
std::cout << " " << val << ": " << ums.count(val) << "次" << std::endl;
counted.insert(val);
}
}
}
// 12.2 配合insert和erase实现元素迁移
void elementMigration()
{
std::unordered_multiset<int> source = { 1, 2, 3, 4, 5, 2, 3 };
std::unordered_multiset<int> dest;
auto it = source.begin();
while (it != source.end())
{
if (*it % 2 == 0)
{
dest.insert(*it);
it = source.erase(it);
}
else
{
++it;
}
}
std::cout << "[元素迁移] source.size() = " << source.size()
<< ", dest.size() = " << dest.size() << std::endl;
}
第三章 完整代码
cpp
#include <iostream>
#include <unordered_set>
#include <string>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
#include <cmath>
/*
* 自定义类型
*/
class Person
{
public:
Person() : m_name(""), m_age(0) {}
Person(const std::string& name, int age) : m_name(name), m_age(age) {}
std::string getName() const { return m_name; }
int getAge() const { return m_age; }
bool operator==(const Person& other) const
{
return m_name == other.m_name && m_age == other.m_age;
}
friend std::ostream& operator<<(std::ostream& os, const Person& p)
{
os << "Person(" << p.m_name << ", " << p.m_age << ")";
return os;
}
private:
std::string m_name;
int m_age;
};
// 自定义哈希函数对象
class PersonHash
{
public:
std::size_t operator()(const Person& p) const
{
std::size_t h1 = std::hash<std::string>{}(p.getName());
std::size_t h2 = std::hash<int>{}(p.getAge());
return h1 ^ (h2 << 1);
}
};
// 自定义相等比较函数对象
class PersonEqual
{
public:
bool operator()(const Person& a, const Person& b) const
{
return a.getName() == b.getName() && a.getAge() == b.getAge();
}
};
// ============================================================
/*
* 1. 构造函数相关
*/
// 1.1 默认构造
void defaultConstructor()
{
std::unordered_multiset<int> ums;
std::cout << "[默认构造] size = " << ums.size() << std::endl;
}
// 1.2 指定桶数量构造
void bucketCountConstructor()
{
std::unordered_multiset<int> ums(20);
std::cout << "[指定桶数量构造] bucket_count = " << ums.bucket_count() << std::endl;
}
// 1.3 带自定义哈希和比较函数构造
void customHashConstructor()
{
std::unordered_multiset<Person, PersonHash, PersonEqual> ums(10, PersonHash(), PersonEqual());
ums.insert(Person("Alice", 25));
std::cout << "[自定义哈希构造] size = " << ums.size() << std::endl;
}
// 1.4 迭代器范围构造
void rangeConstructor()
{
std::vector<int> vec = { 1, 2, 3, 2, 1, 4 };
std::unordered_multiset<int> ums(vec.begin(), vec.end());
std::cout << "[迭代器范围构造] size = " << ums.size() << ", count(1) = " << ums.count(1) << std::endl;
}
// 1.5 初始化列表构造
void initializerListConstructor()
{
std::unordered_multiset<std::string> ums = { "apple", "banana", "apple", "cherry" };
std::cout << "[初始化列表构造] size = " << ums.size() << ", count(apple) = " << ums.count("apple") << std::endl;
}
// 1.6 拷贝构造
void copyConstructor()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3, 2, 1 };
std::unordered_multiset<int> ums2(ums1);
std::cout << "[拷贝构造] ums2.size() = " << ums2.size() << std::endl;
}
// 1.7 移动构造
void moveConstructor()
{
std::unordered_multiset<int> ums1 = { 10, 20, 30, 20 };
std::unordered_multiset<int> ums2(std::move(ums1));
std::cout << "[移动构造] ums2.size() = " << ums2.size()
<< ", ums1.size() = " << ums1.size() << std::endl;
}
// ============================================================
/*
* 2. 赋值运算符
*/
// 2.1 拷贝赋值
void copyAssignment()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2;
ums2 = ums1;
std::cout << "[拷贝赋值] ums2.size() = " << ums2.size() << std::endl;
}
// 2.2 移动赋值
void moveAssignment()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2;
ums2 = std::move(ums1);
std::cout << "[移动赋值] ums2.size() = " << ums2.size() << std::endl;
}
// 2.3 初始化列表赋值
void initializerListAssignment()
{
std::unordered_multiset<int> ums;
ums = { 10, 20, 30, 20, 10 };
std::cout << "[初始化列表赋值] ums.size() = " << ums.size() << std::endl;
}
// ============================================================
/*
* 3. 迭代器
*/
// 3.1 begin / end 遍历
void beginEnd()
{
std::unordered_multiset<int> ums = { 5, 3, 1, 4, 2, 3 };
std::cout << "[begin/end] 元素: ";
for (auto it = ums.begin(); it != ums.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
// 3.2 cbegin / cend 常量遍历
void cbeginCend()
{
std::unordered_multiset<std::string> ums = { "hello", "world", "hello" };
std::cout << "[cbegin/cend] 元素: ";
for (auto it = ums.cbegin(); it != ums.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
// ============================================================
/*
* 4. 容量相关
*/
// 4.1 empty
void checkEmpty()
{
std::unordered_multiset<int> ums1;
std::unordered_multiset<int> ums2 = { 1, 2 };
std::cout << "[empty] ums1.empty() = " << std::boolalpha << ums1.empty()
<< ", ums2.empty() = " << ums2.empty() << std::endl;
}
// 4.2 size
void getSize()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4 };
std::cout << "[size] ums.size() = " << ums.size() << std::endl;
}
// 4.3 max_size
void getMaxSize()
{
std::unordered_multiset<int> ums;
std::cout << "[max_size] ums.max_size() = " << ums.max_size() << std::endl;
}
// ============================================================
/*
* 5. 修改操作
*/
// 5.1 clear
void clearAll()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[clear] 清空前 size = " << ums.size();
ums.clear();
std::cout << ", 清空后 size = " << ums.size() << std::endl;
}
// 5.2 insert - 单值插入
void insertSingle()
{
std::unordered_multiset<int> ums;
auto result = ums.insert(42);
std::cout << "[insert单值] 插入42, *result = " << *result << ", size = " << ums.size() << std::endl;
}
// 5.3 insert - 提示位置插入
void insertWithHint()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto it = ums.begin();
ums.insert(it, 99);
std::cout << "[insert提示] 插入99, size = " << ums.size() << std::endl;
}
// 5.4 insert - 范围插入
void insertRange()
{
std::unordered_multiset<int> ums;
std::vector<int> vec = { 10, 20, 30, 20, 10 };
ums.insert(vec.begin(), vec.end());
std::cout << "[insert范围] size = " << ums.size() << ", count(10) = " << ums.count(10) << std::endl;
}
// 5.5 insert - 初始化列表插入
void insertInitList()
{
std::unordered_multiset<std::string> ums;
ums.insert({ "red", "green", "blue", "red" });
std::cout << "[insert初始化列表] size = " << ums.size() << ", count(red) = " << ums.count("red") << std::endl;
}
// 5.6 emplace
void emplaceElement()
{
std::unordered_multiset<std::string> ums;
auto result = ums.emplace("hello");
std::cout << "[emplace] 构造并插入\"hello\", *result = " << *result
<< ", size = " << ums.size() << std::endl;
}
// 5.7 emplace_hint
void emplaceWithHint()
{
std::unordered_multiset<std::string> ums = { "world" };
auto it = ums.begin();
ums.emplace_hint(it, "hello");
std::cout << "[emplace_hint] size = " << ums.size() << std::endl;
}
// 5.8 erase - 按迭代器删除
void eraseByIterator()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
auto it = ums.find(3);
if (it != ums.end())
{
ums.erase(it);
}
std::cout << "[erase迭代器] 删除3后 size = " << ums.size() << std::endl;
}
// 5.9 erase - 按值删除(删除所有匹配元素)
void eraseByValue()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4 };
auto removed = ums.erase(2);
std::cout << "[erase值] 删除所有2, 移除个数 = " << removed << ", size = " << ums.size() << std::endl;
}
// 5.10 erase - 按范围删除
void eraseByRange()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5, 6 };
auto first = ums.begin();
auto last = ums.begin();
std::advance(last, 3);
ums.erase(first, last);
std::cout << "[erase范围] 删除前3个后 size = " << ums.size() << std::endl;
}
// 5.11 swap
void swapContainers()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 10, 20 };
ums1.swap(ums2);
std::cout << "[swap] ums1.size() = " << ums1.size() << ", ums2.size() = " << ums2.size() << std::endl;
}
// 5.12 extract - 按迭代器提取节点 (C++17)
void extractByIterator()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
auto it = ums.find(3);
if (it != ums.end())
{
auto node = ums.extract(it);
std::cout << "[extract迭代器] 提取节点值 = " << node.value()
<< ", 容器size = " << ums.size() << std::endl;
}
#endif
}
// 5.13 extract - 按值提取节点 (C++17)
void extractByKey()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1 };
auto node = ums.extract(2);
if (!node.empty())
{
std::cout << "[extract值] 提取节点值 = " << node.value()
<< ", 容器size = " << ums.size() << std::endl;
}
#endif
}
// 5.14 merge (C++17)
void mergeContainers()
{
#if _MSVC_LANG >= 201703L
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 4, 5, 6, 5 };
ums1.merge(ums2);
std::cout << "[merge] ums1.size() = " << ums1.size()
<< ", ums2.size() = " << ums2.size() << std::endl;
#endif
}
// ============================================================
/*
* 6. 查找操作
*/
// 6.1 count
void countElements()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4, 1 };
std::cout << "[count] count(1) = " << ums.count(1)
<< ", count(2) = " << ums.count(2)
<< ", count(99) = " << ums.count(99) << std::endl;
}
// 6.2 find
void findElement()
{
std::unordered_multiset<int> ums = { 10, 20, 30, 20 };
auto it = ums.find(20);
if (it != ums.end())
{
std::cout << "[find] 找到元素: " << *it << std::endl;
}
auto it2 = ums.find(99);
if (it2 == ums.end())
{
std::cout << "[find] 未找到99" << std::endl;
}
}
// 6.3 contains (C++20)
void containsElement()
{
#if _MSVC_LANG >= 202002L
std::unordered_multiset<std::string> ums = { "apple", "banana", "apple" };
std::cout << "[contains] contains(\"apple\") = " << std::boolalpha << ums.contains("apple")
<< ", contains(\"grape\") = " << ums.contains("grape") << std::endl;
#endif
}
// 6.4 equal_range
void equalRangeElements()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 2, 1, 4, 2 };
auto range = ums.equal_range(2);
std::cout << "[equal_range] 元素2的所有出现: ";
for (auto it = range.first; it != range.second; ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
// ============================================================
/*
* 7. 桶接口
*/
// 7.1 bucket_count
void getBucketCount()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[bucket_count] bucket_count = " << ums.bucket_count() << std::endl;
}
// 7.2 max_bucket_count
void getMaxBucketCount()
{
std::unordered_multiset<int> ums;
std::cout << "[max_bucket_count] max_bucket_count = " << ums.max_bucket_count() << std::endl;
}
// 7.3 bucket_size
void getBucketSize()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[bucket_size] 桶" << i << " 有 " << ums.bucket_size(i) << " 个元素" << std::endl;
}
}
}
// 7.4 bucket - 获取元素所在桶编号
void getBucketIndex()
{
std::unordered_multiset<int> ums = { 10, 20, 30 };
std::cout << "[bucket] 元素10在桶 " << ums.bucket(10)
<< ", 元素20在桶 " << ums.bucket(20) << std::endl;
}
// 7.5 begin(n) / end(n) - 桶迭代器
void bucketIterators()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[桶迭代器] 桶" << i << ": ";
for (auto it = ums.begin(i); it != ums.end(i); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
}
}
// 7.6 cbegin(n) / cend(n) - 桶常量迭代器
void bucketConstIterators()
{
std::unordered_multiset<int> ums = { 11, 22, 33, 44 };
for (std::size_t i = 0; i < ums.bucket_count(); ++i)
{
if (ums.bucket_size(i) > 0)
{
std::cout << "[桶常量迭代器] 桶" << i << ": ";
for (auto it = ums.cbegin(i); it != ums.cend(i); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
}
}
// ============================================================
/*
* 8. 哈希策略
*/
// 8.1 load_factor
void getLoadFactor()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[load_factor] load_factor = " << ums.load_factor()
<< ", bucket_count = " << ums.bucket_count() << std::endl;
}
// 8.2 max_load_factor(获取)
void getMaxLoadFactor()
{
std::unordered_multiset<int> ums;
std::cout << "[max_load_factor获取] max_load_factor = " << ums.max_load_factor() << std::endl;
}
// 8.3 max_load_factor(设置)
void setMaxLoadFactor()
{
std::unordered_multiset<int> ums;
ums.max_load_factor(2.0f);
std::cout << "[max_load_factor设置] 设置后 max_load_factor = " << ums.max_load_factor() << std::endl;
}
// 8.4 rehash
void rehashBuckets()
{
std::unordered_multiset<int> ums = { 1, 2, 3, 4, 5 };
std::cout << "[rehash] 之前 bucket_count = " << ums.bucket_count();
ums.rehash(50);
std::cout << ", 之后 bucket_count = " << ums.bucket_count() << std::endl;
}
// 8.5 reserve
void reserveCapacity()
{
std::unordered_multiset<int> ums;
ums.reserve(100);
std::cout << "[reserve] reserve(100)后 bucket_count = " << ums.bucket_count() << std::endl;
}
// ============================================================
/*
* 9. 观察器
*/
// 9.1 hash_function
void getHashFunction()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto hasher = ums.hash_function();
std::cout << "[hash_function] hash(42) = " << hasher(42) << std::endl;
}
// 9.2 key_eq
void getKeyEq()
{
std::unordered_multiset<int> ums = { 1, 2, 3 };
auto keyEqual = ums.key_eq();
std::cout << "[key_eq] key_eq(1, 1) = " << std::boolalpha << keyEqual(1, 1)
<< ", key_eq(1, 2) = " << keyEqual(1, 2) << std::endl;
}
// ============================================================
/*
* 10. 非成员函数
*/
// 10.1 operator==
void operatorEqual()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 1, 2, 3 };
std::unordered_multiset<int> ums3 = { 1, 2, 4 };
std::cout << "[operator==] ums1 == ums2: " << std::boolalpha << (ums1 == ums2)
<< ", ums1 == ums3: " << (ums1 == ums3) << std::endl;
}
// 10.2 operator!=
void operatorNotEqual()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 4, 5, 6 };
std::cout << "[operator!=] ums1 != ums2: " << std::boolalpha << (ums1 != ums2) << std::endl;
}
// 10.3 std::swap (非成员)
void stdSwapContainers()
{
std::unordered_multiset<int> ums1 = { 1, 2, 3 };
std::unordered_multiset<int> ums2 = { 10, 20 };
std::swap(ums1, ums2);
std::cout << "[std::swap] ums1.size() = " << ums1.size()
<< ", ums2.size() = " << ums2.size() << std::endl;
}
// ============================================================
/*
* 11. 自定义类型综合使用
*/
void customTypeUsage()
{
std::unordered_multiset<Person, PersonHash, PersonEqual> ums;
ums.insert(Person("Alice", 25));
ums.insert(Person("Bob", 30));
ums.insert(Person("Alice", 25)); // 允许重复
ums.insert(Person("Charlie", 35));
std::cout << "[自定义类型] 所有元素: ";
for (const auto& p : ums)
{
std::cout << p << " ";
}
std::cout << std::endl;
Person target("Alice", 25);
std::cout << "[自定义类型] count(Alice,25) = " << ums.count(target) << std::endl;
auto range = ums.equal_range(target);
std::cout << "[自定义类型] equal_range(Alice,25) 元素: ";
for (auto it = range.first; it != range.second; ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
// ============================================================
/*
* 12. 综合应用场景
*/
// 12.1 统计元素频率
void frequencyCounting()
{
std::vector<int> data = { 1, 2, 3, 2, 1, 4, 3, 2, 1, 5 };
std::unordered_multiset<int> ums(data.begin(), data.end());
std::cout << "[频率统计] 各元素出现次数:" << std::endl;
std::unordered_multiset<int> counted;
for (const auto& val : data)
{
if (counted.find(val) == counted.end())
{
std::cout << " " << val << ": " << ums.count(val) << "次" << std::endl;
counted.insert(val);
}
}
}
// 12.2 配合insert和erase实现元素迁移
void elementMigration()
{
std::unordered_multiset<int> source = { 1, 2, 3, 4, 5, 2, 3 };
std::unordered_multiset<int> dest;
auto it = source.begin();
while (it != source.end())
{
if (*it % 2 == 0)
{
dest.insert(*it);
it = source.erase(it);
}
else
{
++it;
}
}
std::cout << "[元素迁移] source.size() = " << source.size()
<< ", dest.size() = " << dest.size() << std::endl;
}
// ============================================================
int main()
{
std::cout << "======================== 构造函数 ========================" << std::endl;
defaultConstructor();
bucketCountConstructor();
customHashConstructor();
rangeConstructor();
initializerListConstructor();
copyConstructor();
moveConstructor();
std::cout << "\n======================== 赋值运算符 ========================" << std::endl;
copyAssignment();
moveAssignment();
initializerListAssignment();
std::cout << "\n======================== 迭代器 ========================" << std::endl;
beginEnd();
cbeginCend();
std::cout << "\n======================== 容量 ========================" << std::endl;
checkEmpty();
getSize();
getMaxSize();
std::cout << "\n======================== 修改操作 ========================" << std::endl;
clearAll();
insertSingle();
insertWithHint();
insertRange();
insertInitList();
emplaceElement();
emplaceWithHint();
eraseByIterator();
eraseByValue();
eraseByRange();
swapContainers();
extractByIterator();
extractByKey();
mergeContainers();
std::cout << "\n======================== 查找操作 ========================" << std::endl;
countElements();
findElement();
containsElement();
equalRangeElements();
std::cout << "\n======================== 桶接口 ========================" << std::endl;
getBucketCount();
getMaxBucketCount();
getBucketSize();
getBucketIndex();
bucketIterators();
bucketConstIterators();
std::cout << "\n======================== 哈希策略 ========================" << std::endl;
getLoadFactor();
getMaxLoadFactor();
setMaxLoadFactor();
rehashBuckets();
reserveCapacity();
std::cout << "\n======================== 观察器 ========================" << std::endl;
getHashFunction();
getKeyEq();
std::cout << "\n======================== 非成员函数 ========================" << std::endl;
operatorEqual();
operatorNotEqual();
stdSwapContainers();
std::cout << "\n======================== 自定义类型 ========================" << std::endl;
customTypeUsage();
std::cout << "\n======================== 综合应用 ========================" << std::endl;
frequencyCounting();
elementMigration();
return 0;
}