Почему std:: unordered map имеет резервный метод?
Согласно этому вы не можете зарезервировать место для std::map
:
Нет, элементы карты хранятся внутри древовидной структуры. Невозможно построить дерево, пока вы не знаете ключи и значения они должны быть сохранены.
Из этого очевидно, почему std::map
будет не хватать метода reserve()
, который он делает на cppreference.com однако ... , std::unordered_map
есть ли у Метод reserve()
, но когда я пытаюсь использовать его с operator[]
, insert()
или emplace()
они все идут выделить память, несмотря на то, что я вызвал reserve()
первым.
Что это такое? Почему reserve()
должным образом не зарезервирует необходимое пространство? И если, как и в случае с картой, вы не можете заранее выделить память, то почему std::unordered_map
вообще имеет метод reserve()
?
1 ответ:
Контейнер
unordered_map
имеет методreserve
, поскольку он реализован с использованием ведер, а не дерева, как вmap
.Ведро-это:
Слот во внутренней хэш-таблице контейнера, которому присваиваются элементы на основе хэш-значения их ключа. Ведра нумеруются от 0 до (bucket_count-1). (Источник )
Одно ведро содержит переменное количество элементов. Это число основано на
load_factor
. Когдаload_factor
достигает a определенный порог, контейнер увеличивает количество ведер и перерисовывает карту.Когда вы звоните
reserve(n)
, контейнер создает достаточное количество ведер, чтобы вместить по крайней мереn
элементов.Это в отличие от
rehash(n)
который непосредственно устанавливает количество ведер вn
и запускает перестройку всей хэш-таблицы.Смотрите также: предварительное выделение ведер в C++ unordered_map
Редактировать в ответ на Комментарии
Поскольку я не знаю точного ответа на вопрос, поставленный в комментариях, и поскольку мои предварительные исследования не оказались плодотворными, я решил проверить это экспериментально. Для справки, вопрос сводится к следующему:Не могли бы вы объяснить, является ли резервирование ячеек для n элементов тем же самым, что и выделение памяти для n элементов?
Согласно этому ответу , точно извлекая размер выделенного пространства в an
Во-первых, мой тест выглядит следующим образом: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; }
Там, где указывают комментарии, я сделал снимок памяти.
Результаты были следующими:
Снимок А:
┌──────────────┬──────────────┬──────────────┐ | 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
, которая, как обсуждалось ранее, полностью перестраивает карту.