this によると、_std::map
_のスペースを予約することはできません:
いいえ、マップのメンバーは内部的にツリー構造で保存されています。保存するキーと値がわかるまで、ツリーを構築する方法はありません。
これから、_std::map
_にreserve()
メソッドがない理由は明らかです。これは、cppreference.comで実行されます。ただし、_std::unordered_map
_doesにはreserve()
メソッドがありますが、_operator[]
_で使用しようとすると、insert()
またはemplace()
最初にreserve()
を呼び出したにもかかわらず、それらはすべてメモリの割り当てに移動します。
これどうしたの?必要なスペースをreserve()
が適切に予約しないのはなぜですか?また、事前にメモリを割り当てることができないマップの場合、なぜ_std::unordered_map
_にreserve()
メソッドがあるのですか?
_unordered_map
_コンテナにはreserve
メソッドがあります。これは、map
のようなツリーではなく、バケットを使用して実装されているためです。
バケットは:
キーのハッシュ値に基づいて要素が割り当てられる、コンテナの内部ハッシュテーブル内のスロット。バケットの番号は0から(bucket_count-1)までです。 ( ソース )
1つのバケットは、可変数のアイテムを保持します。この数は _load_factor
_ に基づいています。 _load_factor
_が特定のしきい値に達すると、コンテナはバケットの数を増やし、マップを再ハッシュします。
reserve(n)
を呼び出すと、コンテナは少なくともn
アイテムを保持するのに十分なバケットを作成します。
これは、バケット数をn
に直接設定し、ハッシュテーブル全体の再構築をトリガーする rehash(n)
とは対照的です。
参照: C++ unordered_mapでのバケットの事前割り当て
コメントに応じて編集
コメントで提起された質問に対する正確な答えがわからないため、また、私の予備調査が実を結ばなかったため、実験的にテストすることにしました。
参考までに、質問は次のようになります。
N要素のバケットの予約がn要素のメモリの割り当てと同じかどうか説明していただけませんか?
この答え によると、_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;
}
_
コメントが示すところ、私はメモリのスナップショットを撮りました。
結果は次のとおりです。
スナップショット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
にはメリットがありますか?私は2つの理由で「はい」と言います。(1)バケットのメモリを事前に割り当てること、および(2)rehash
の必要性を防ぐことができます。