C++ 命名要求: UnorderedAssociativeContainer (C++11 起)
无序关联容器是 Container,它根据键提供对象的快速查找。在最坏情况下,复杂度是线性的,但对于大多数操作,平均速度要快得多。
无序关联容器通过 `Key` 参数化;`Hash` 是一个 Hash 函数对象,它作为 `Key` 的哈希函数;`Pred` 是一个 BinaryPredicate,用于评估 `Key` 之间的等价性。std::unordered_map 和 std::unordered_multimap 还有一个与 `Key` 关联的映射类型 `T`。
如果两个 `Key` 根据 `Pred` 相等,则 `Hash` 必须为两个键返回相同的值。
如果 `Hash::is_transparent` 和 `Pred::is_transparent` 都存在并且各自命名一个类型,则成员函数 `find`、`contains`、`count`、`equal_range` 和 `bucket` 接受 `Key` 以外类型的参数,并期望 `Hash` 可以用这些类型的值调用,并且 `Pred` 是一个透明的比较函数,例如 std::equal_to<>。 |
(C++20 起) |
std::unordered_map 和 std::unordered_set 最多可以包含一个具有给定键的元素,而 std::unordered_multiset 和 std::unordered_multimap 可以有多个具有相同键的元素(在迭代时它们必须始终相邻)。
对于 std::unordered_set 和 std::unordered_multiset,值类型与键类型相同,并且 `iterator` 和 `const_iterator` 都是常量迭代器。对于 std::unordered_map 和 std::unordered_multimap,值类型为 std::pair<const Key, T>。
无序关联容器中的元素被组织成桶,具有相同哈希值的键将最终落在同一个桶中。当容器的大小增加时,桶的数量也会增加,以使每个桶中的平均元素数量保持在某个值以下。
再哈希会使迭代器失效,并可能导致元素在不同的桶中重新排列,但它不会使对元素的引用失效。
无序关联容器满足 AllocatorAwareContainer 的要求。对于 std::unordered_map 和 std::unordered_multimap,AllocatorAwareContainer 中 `value_type` 的要求适用于 `key_type` 和 `mapped_type`(不适用于 `value_type`)。
目录 |
[编辑] 要求
图例 | |
X
|
一个无序关联容器类 |
a | 类型为 X 的值 |
a2 | 一个类型为 与类型 X 兼容的节点 的值 |
b | 类型为 X 或 const X 的值 |
a_uniq | 当 X 支持唯一键时,类型为 X 的值 |
a_eq | 当 X 支持等效键时,类型为 X 的值 |
a_tran | 当限定符标识符 X::key_equal::is_transparent 和 X::hasher::is_transparent 都有效且表示类型时,类型为 X 或 const X 的值 |
i, j | 引用 value_type 的输入迭代器 |
[ i, j)
|
一个有效范围 |
rg (C++23 起) | 一个类型 R 的值,它建模 container-compatible-range<value_type> |
p, q2 | 指向 a 的有效常量迭代器 |
q, q1 | 指向 a 的有效可解引用常量迭代器 |
r | 指向 a 的有效可解引用迭代器 |
[ q1, q2)
|
在 a 中的一个有效范围 |
il | 类型为 std::initializer_list<value_type> 的值 |
t | 类型为 X::value_type 的值 |
k | 类型为 key_type 的值 |
hf | 类型为 hasher 或 const hasher 的值 |
eq | 类型为 key_equal 或 const key_equal 的值 |
ke | 一个值,使得
其中 r1 和 r2 是 a_tran 中元素的键 |
kx (C++23 起) | 一个值,使得
其中 r1 和 r2 是 a_tran 中元素的键 |
n | 类型为 size_type 的值 |
z | 类型为 float 的值 |
nh (C++17 起) | 类型为 X::node_type 的右值 |
[编辑] 成员类型
名称 | 类型 | 要求 | 注意 |
---|---|---|---|
X::key_type
|
Key
|
||
X::mapped_type
|
T
|
std::unordered_map 和 std::unordered_multimap 仅适用 | |
X::value_type
|
Key
|
std::unordered_set 和 std::unordered_multiset 仅适用。可擦除于 X |
|
std::pair<const Key, T> | std::unordered_map 和 std::unordered_multimap 仅适用。可擦除于 X |
||
X::hasher
|
Hash(哈希)
|
Hash(哈希) | |
X::key_equal
|
Pred
|
可复制构造;二元谓词,它接受两个 Key 类型的参数并表示等价关系 |
|
X::local_iterator
|
LegacyIterator(传统迭代器) | 类别和类型与 X::iterator 相同 |
可用于遍历单个桶,但不能跨桶遍历 |
X::const_local_iterator
|
LegacyIterator(传统迭代器) | 类别和类型与 X::const_iterator 相同 | |
X::node_type (C++17 起) |
node-handle 类模板的特化 | 公共嵌套类型与 X 中相应的类型相同 |
[编辑] 成员函数和运算符
表达式 | 结果 | 前置条件 | 效果 | 返回 | 复杂度 |
---|---|---|---|---|---|
X(n, hf, eq) | 构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,eq 作为键相等谓词 | O(n) | |||
X(n, hf) | key_equal 是 可默认构造的 |
构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,key_equal() 作为键相等谓词 | O(n) | ||
X(n) | hasher 和 key_equal 是 可默认构造的 |
构造一个空容器,至少有 n 个桶,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词 | O(n) | ||
X a = X(); X a; |
hasher 和 key_equal 是 可默认构造的 |
构造一个空容器,桶的数量未指定,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词 | 常量 | ||
X(i, j, n, hf, eq) | value_type 是 可就地构造到 X 的,从 *i 开始 |
构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,eq 作为键相等谓词,并从 [ i, j) 插入元素 |
平均情况 O(N)(N 为 std::distance(i, j)),最坏情况 O(N2) | ||
X(i, j, n, hf) | key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *i 开始 |
构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,key_equal() 作为键相等谓词,并从 [ i, j) 插入元素 |
平均情况 O(N)(N 为 std::distance(i, j)),最坏情况 O(N2) | ||
X(i, j, n) | hasher 和 key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *i 开始 |
构造一个空容器,至少有 n 个桶,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词,并从 [ i, j) 插入元素 |
平均情况 O(N)(N 为 std::distance(i, j)),最坏情况 O(N2) | ||
X(i, j) | hasher 和 key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *i 开始 |
构造一个空容器,桶的数量未指定,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词,并从 [ i, j) 插入元素 |
平均情况 O(N)(N 为 std::distance(i, j)),最坏情况 O(N2) | ||
X(std::from_range, rg, n, hf, eq) (C++23 起) |
value_type 是 可就地构造到 X 的,从 *ranges::begin(rg) 开始 |
构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,eq 作为键相等谓词,并从 rg 插入元素 | 平均情况 O(N)(N 为 ranges::distance(rg)),最坏情况 O(N2) | ||
X(std::from_range, rg, n, hf) (C++23 起) |
key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *ranges::begin(rg) 开始 |
构造一个空容器,至少有 n 个桶,使用 hf 作为哈希函数,key_equal() 作为键相等谓词,并从 rg 插入元素 | 平均情况 O(N)(N 为 ranges::distance(rg)),最坏情况 O(N2) | ||
X(std::from_range, rg, n) (C++23 起) |
hasher 和 key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *ranges::begin(rg) 开始 |
构造一个空容器,至少有 n 个桶,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词,并从 rg 插入元素 | 平均情况 O(N)(N 为 ranges::distance(rg)),最坏情况 O(N2) | ||
X(std::from_range, rg) (C++23 起) |
hasher 和 key_equal 是 可默认构造的。value_type 是 可就地构造到 X 的,从 *ranges::begin(rg) 开始 |
构造一个空容器,桶的数量未指定,使用 hasher() 作为哈希函数,key_equal() 作为键相等谓词,并从 rg 插入元素 | 平均情况 O(N)(N 为 ranges::distance(rg)),最坏情况 O(N2) | ||
X(il) | X(il.begin(), il.end()) | ||||
X(il, n) | X(il.begin(), il.end(), n) | ||||
X(il, n, hf) | X(il.begin(), il.end(), n, hf) | ||||
X(il, n, hf, eq) | X(il.begin(), il.end(), n, hf, eq) | ||||
X(b) | 容器;复制哈希函数、谓词和最大负载因子 | 平均情况为 b.size() 的线性复杂度,最坏情况为 O(N2) | |||
a = b | X&
|
容器;复制哈希函数、谓词和最大负载因子 | 平均情况为 b.size() 的线性复杂度,最坏情况为 O(N2) | ||
a = il | X&
|
value_type 可 复制插入到 X 中,且可 复制赋值 |
将范围 [ il.begin(), il.end()) 赋值给 a。 a 中所有现有元素要么被赋值,要么被销毁 |
平均情况为 il.size() 的线性复杂度,最坏情况为 O(N2) | |
b.hash_function() | 哈希器
|
b 的哈希函数 | 常量 | ||
b.key_eq() | 键相等
|
b 的键相等谓词 | 常量 | ||
a_uniq.emplace(args) | std::pair< 迭代器, bool> |
value_type 可 就地构造到 X 的,从 args 开始 |
仅当容器中没有与 t 的键等效的元素时,才插入使用 std::forward<Args>(args)... 构造的 value_type 对象 t |
返回对的 bool 分量当且仅当插入发生时为 true,并且对的迭代器分量指向键与 t 的键等效的元素 | 平均情况 O(1),最坏情况 O(a_uniq.size()) |
a_eq.emplace(args) | 迭代器
|
value_type 可 就地构造到 X 的,从 args 开始 |
插入使用 std::forward<Args>(args)... 构造的 value_type 对象 t |
指向新插入元素的迭代器 | 平均情况 O(1),最坏情况 O(a_eq.size()) |
a.emplace_hint(p, args) | 迭代器
|
value_type 可 就地构造到 X 的,从 args 开始 |
a.emplace( std::forward<Args>(args)...) |
指向键与新插入元素等效的元素的迭代器。const_iterator p 是一个提示,指示搜索应从何处开始。允许实现忽略该提示 |
平均情况 O(1),最坏情况 O(a.size()) |
a_uniq.insert(t) | std::pair< 迭代器, bool> |
如果 t 是一个非 const 右值,value_type 可 移动插入到 X 中;否则,value_type 可 复制插入到 X 中 |
当且仅当容器中没有与 t 的键等效的元素时,才插入 t | 返回对的 bool 分量表示是否发生插入,并且 iterator 分量指向键与 t 的键等效的元素 |
平均情况 O(1),最坏情况 O(a_uniq.size()) |
a_eq.insert(t) | 迭代器
|
如果 t 是一个非 const 右值,value_type 可 移动插入到 X 中;否则,value_type 可 复制插入到 X 中 |
插入 t | 指向新插入元素的迭代器 | 平均情况 O(1),最坏情况 O(a_eq.size()) |
a.insert(p, t) | 迭代器
|
如果 t 是一个非 const 右值,value_type 可 移动插入到 X 中;否则,value_type 可 复制插入到 X 中 |
a.insert(t)。迭代器 p 是一个提示,指示搜索应从何处开始。允许实现忽略该提示 | 指向键与 t 的键等效的元素的迭代器 | 平均情况 O(1),最坏情况 O(a.size()) |
a.insert(i, j) | void | value_type 是 可就地构造到 X 的,从 *i 开始。 i 和 j 都不是 a 的迭代器 |
对于范围中的每个元素,执行 a.insert(t)[ i, j)
|
平均情况 O(N),其中 N 为 std::distance(i, j),最坏情况 O(N·(a.size() + 1)) | |
a.insert_range(rg) (C++23 起) |
void | value_type 是 可就地构造到 X 的,从 *ranges::begin(rg) 开始。 rg 和 a 不重叠 |
对于 rg 中的每个元素 t,执行 a.insert(t) | 平均情况 O(N),其中 N 为 ranges::distance(rg),最坏情况 O(N·(a.size() + 1)) | |
a.insert(il) | a.insert(il.begin(), il.end()) | ||||
a_uniq.insert(nh) (C++17 起) |
insert_return_type
|
nh 为空或 a_uniq.get_allocator() |
如果 nh 为空,则无效果。否则,当且仅当容器中没有与 nh.key() 的键等效的元素时,插入 nh 所拥有的元素。确保:如果 nh 为空,则 inserted 为 false,position 为 end(),且 node 为空。否则,如果插入成功,则 inserted 为 true,position 指向插入的元素,且 node 为空;如果插入失败,则 inserted 为 false,node 具有 nh 的先前值,且 position 指向键与 nh.key() 的键等效的元素 | 平均情况 O(1),最坏情况 O(a_uniq.size()) | |
a_eq.insert(nh) (C++17 起) |
迭代器
|
nh 为空或 a_eq.get_allocator() |
如果 nh 为空,则无效果并返回 a_eq.end()。否则,插入 nh 所拥有的元素并返回指向新插入元素的迭代器。确保:nh 为空 | 平均情况 O(1),最坏情况 O(a_eq.size()) | |
a.insert(q, nh) (C++17 起) |
迭代器
|
nh 为空或 a.get_allocator() |
如果 nh 为空,则无效果并返回 a.end()。否则,当且仅当具有唯一键的容器中没有与 nh.key() 的键等效的元素时,插入 nh 所拥有的元素;对于具有等效键的容器,始终插入 nh 所拥有的元素。迭代器 q 是一个提示,指示搜索应从何处开始。允许实现忽略该提示。确保:如果插入成功,则 nh 为空,如果插入失败,则不变 | 指向键与 nh.key() 的键等效的元素的迭代器 | 平均情况 O(1),最坏情况 O(a.size()) |
a.extract(k) (C++17 起) |
node_type
|
删除容器中键与 k 等效的元素 | 如果找到,则为拥有该元素的 node_type ,否则为空的 node_type |
平均情况 O(1),最坏情况 O(a.size()) | |
a_tran.extract(kx) (C++23 起) |
node_type
|
删除容器中键与 kx 等效的元素 | 如果找到,则为拥有该元素的 node_type ,否则为空的 node_type |
平均情况 O(1),最坏情况 O(a_tran.size()) | |
a.extract(q) (C++17 起) |
node_type
|
删除 q 指向的元素 | 拥有该元素的 node_type |
平均情况 O(1),最坏情况 O(a.size()) | |
a.merge(a2) (C++17 起) |
void | a.get_allocator() == a2.get_allocator() |
尝试提取 a2 中的每个元素并使用 a 的哈希函数和键相等谓词将其插入 a。在具有唯一键的容器中,如果 a 中存在一个键与来自 a2 的元素的键等效的元素,则该元素不会从 a2 中提取。确保:指向 a2 中已转移元素的指针和引用引用相同的元素,但作为 a 的成员。引用已转移元素的迭代器以及所有引用 a 的迭代器都将失效,但 a2 中剩余元素的迭代器将保持有效 | 平均情况 O(N),其中 N 为 a2.size(),最坏情况 O(N·(a.size() + 1)) | |
a.erase(k) | size_type
|
擦除所有键与 k 等效的元素 | 被擦除的元素数量 | 平均情况 O(a.count(k)),最坏情况 O(a.size()) | |
a_tran.erase(kx) (C++23 起) |
size_type
|
擦除所有键与 kx 等效的元素 | 被擦除的元素数量 | 平均情况 O(a_tran.count(kx)),最坏情况 O(a_tran.size()) | |
a.erase(q) | 迭代器
|
擦除 q 指向的元素 | 紧接在擦除前 q 之后的迭代器 | 平均情况 O(1),最坏情况 O(a.size()) | |
a.erase(r) (C++17 起) |
迭代器
|
擦除 r 指向的元素 | 紧接在擦除前 r 之后的迭代器 | 平均情况 O(1),最坏情况 O(a.size()) | |
a.erase(q1, q2) | 迭代器
|
擦除范围内的所有元素[ q1, q2)
|
紧接在擦除前被擦除元素之后的迭代器 | 平均情况 O(N),其中 N 为 std::distance(q1, q2),最坏情况 O(a.size()) | |
a.clear() | void | 擦除容器中的所有元素。确保:a.empty() 为 true | 线性复杂度,与 a.size() 相关 | ||
b.find(k) | iterator ;对于常量 b,为 const_iterator |
指向键与 k 等效的元素的迭代器,如果不存在此类元素,则为 b.end() | 平均情况 O(1),最坏情况 O(b.size()) | ||
a_tran.find(ke) (C++17 起)? |
iterator ;对于常量 a_tran,为 const_iterator |
指向键与 ke 等效的元素的迭代器,如果不存在此类元素,则为 a_tran.end() | 平均情况 O(1),最坏情况 O(a_tran.size()) | ||
b.count(k) | size_type
|
键与 k 等效的元素数量 | 平均情况 O(b.count(k)),最坏情况 O(b.size()) | ||
a_tran.count(ke) (C++17 起)? |
size_type
|
键与 ke 等效的元素数量 | 平均情况 O(a_tran.count(ke)),最坏情况 O(a_tran.size()) | ||
b.contains(k) (C++20 起)? |
b.find(k) != b.end() | ||||
a_tran.contains(ke) (C++20 起)? |
a_tran.find(ke) != a_tran.end() | ||||
b.equal_range(k) | std::pair< 迭代器, 迭代器>; std::pair< |
包含所有键与 k 等效的元素的范围。如果不存在此类元素,则返回 std::make_pair( |
平均情况 O(b.count(k)),最坏情况 O(b.size()) | ||
a_tran.equal_range(ke) (C++20 起)? |
std::pair< 迭代器, 迭代器>; std::pair< |
包含所有键与 ke 等效的元素的范围。如果不存在此类元素,则返回 std::make_pair( |
平均情况 O(a_tran.count(ke)),最坏情况 O(a_tran.size()) | ||
b.bucket_count() | size_type
|
b 包含的桶的数量 | 常量 | ||
b.max_bucket_count() | size_type
|
b 可以包含的桶数量的上限 | 常量 | ||
b.bucket(k) | size_type
|
b.bucket_count() > 0 | 键与 k 等效的元素(如果存在)将位于的桶的索引。返回值在 [ 0, b.bucket_count()) 范围内 |
常量 | |
a_tran.bucket(ke) | size_type
|
a_tran. bucket_count() > 0 |
键与 ke 等效的元素(如果存在)将位于的桶的索引。返回值必须在 [ 0, a_tran.bucket_count()) 范围内 |
常量 | |
b.bucket_size(n) | size_type
|
n 在 [ 0, b.bucket_count()) 范围内 |
第 n 个桶中的元素数量 | O(b.bucket_size(n)) | |
b.begin(n) | local_iterator ;对于常量 b,为 const_local_iterator |
n 在 [ 0, b.bucket_count()) 范围内 |
指向桶中第一个元素的迭代器。如果桶为空,则 b.begin(n) == b.end(n) | 常量 | |
b.end(n) | local_iterator ;对于常量 b,为 const_local_iterator |
n 在 [ 0, b.bucket_count()) 范围内 |
桶的末尾值迭代器 | 常量 | |
b.cbegin(n) | const_local_iterator
|
n 在 [ 0, b.bucket_count()) 范围内 |
指向桶中第一个元素的迭代器。如果桶为空,则 b.cbegin(n) == b.cend(n) | 常量 | |
b.cend(n) | const_local_iterator
|
n 在 [ 0, b.bucket_count()) 范围内 |
桶的末尾值迭代器 | 常量 | |
b.load_factor() | float | 每个桶的平均元素数量 | 常量 | ||
b.max_load_factor() | float | 一个正数,容器试图将负载因子保持在该数之下或等于该数。容器在必要时自动增加桶的数量,以使负载因子低于该数 | 常量 | ||
a.max_load_factor(z) | void | z 为正数。可能会更改容器的最大负载因子,使用 z 作为提示 | 常量 | ||
a.rehash(n) | void | 确保 a.bucket_count() >= |
平均情况线性复杂度,与 a.size() 相关,最坏情况 O(N2) | ||
a.reserve(n) | a.rehash(std::ceil( n / a.max_load_factor())) |
本节不完整 原因:关于成员函数的要求。 |
[编辑] 标准库
以下标准库容器满足 UnorderedAssociativeContainer 要求
(C++11) |
由键哈希的唯一键集合 (类模板) |
(C++11) |
键的集合,按键哈希 (类模板) |
(C++11) |
键值对的集合,按键哈希,键是唯一的 (类模板) |
(C++11) |
键值对集合,按键哈希 (类模板) |
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 2156 | C++11 | 再哈希后的负载因子只能是 严格低于最大负载因子 |
允许相等 |