unordered_map 容器有一个reserve 方法,因为它是使用存储桶实现的,而不是map 中的树。
一个桶是:
容器内部哈希表中的一个槽,元素根据其键的哈希值分配到该槽。桶的编号从 0 到 (bucket_count-1)。 (source)
单个存储桶包含可变数量的项目。此数字基于load_factor。当load_factor达到一定的阈值时,容器会增加bucket的数量并对map进行rehash。
当您调用 reserve(n) 时,容器会创建足够的存储桶以至少容纳 n 项。
这与rehash(n) 不同,后者直接将桶数设置为n 并触发整个哈希表的重建。
另见:Pre-allocating buckets in a C++ unordered_map
根据评论进行编辑
由于我不知道 cmets 提出的问题的确切答案,而且我的初步研究没有证明有成果,所以我决定进行实验测试。
作为参考,问题归结为:
您能否解释一下为 n 个元素保留存储桶是否与为 n 个元素分配内存相同?
根据this answer,准确检索unordered_map 中分配空间的大小是棘手且不可靠的。所以我决定使用 Visual Studio 2015 的诊断工具。
首先,我的测试用例如下:
#include <unordered_map>
#include <cstdint>
struct Foo
{
Foo() : x(0.0f), y(0.0f), z(0.0f) { }
float x;
float y;
float z;
};
int32_t main(int32_t argc, char** argv)
{
std::unordered_map<uint32_t, Foo> mapNoReserve;
std::unordered_map<uint32_t, Foo> mapReserve;
// --> Snapshot A
mapReserve.reserve(1000);
// --> Snapshot B
for(uint32_t i = 0; i < 1000; ++i)
{
mapNoReserve.insert(std::make_pair(i, Foo()));
mapReserve.insert(std::make_pair(i, Foo()));
}
// -> Snapshot C
return 0;
}
在 cmets 指示的地方,我拍了一张内存快照。
结果如下:
快照 A:
┌──────────────┬──────────────┬──────────────┐
| Map | Size (Bytes) | Bucket Count |
|--------------|--------------|--------------|
| mapNoReserve | 64 | 8 |
| mapReserve | 64 | 8 |
└──────────────┴──────────────┴──────────────┚
快照 B:
┌──────────────┬──────────────┬──────────────┐
| Map | Size (Bytes) | Bucket Count |
|--------------|--------------|--------------|
| mapNoReserve | 64 | 8 |
| mapReserve | 8231 | 1024 |
└──────────────┴──────────────┴──────────────┚
快照 C:
┌──────────────┬──────────────┬──────────────┐
| Map | Size (Bytes) | Bucket Count |
|--------------|--------------|--------------|
| mapNoReserve | 24024 | 1024 |
| mapReserve | 24024 | 1024 |
└──────────────┴──────────────┴──────────────┚
解读:
从快照中可以看出,一旦我们开始向它们添加元素,这两个地图的大小似乎都在增长,甚至是名为 reserve 的地图。
那么reserve 是否提供了一个好处,即使内存仍然被分配?我会说是的,原因有两个:(1) 它为存储桶预先分配内存,(2) 它可以防止需要 rehash,如前所述,它会完全重建地图。