Карты реализованы в виде деревьев двоичного поиска , и каждый узел (в дополнение к полезным данным) обычно хранит 3 указателя (налевый ребенок, правый ребенок и родитель).
Неупорядоченные карты реализованы в виде хеш-таблиц , где каждый узел находится в связанном списке.В случае односвязного списка (который принадлежит соответствующему сегменту) имеется только 1 указатель на узел. ОБНОВЛЕНИЕ : Однако для каждого сегмента также имеется дополнительный указатель.В идеальном случае, когда нет столкновений, будет 2 указателя на элемент, хранящийся в памяти.
Обратите внимание, что 2 int
s обычно занимает 8байтов, так же, как один указатель.
Например, посмотрите на реализацию GNU libstdc ++.Узел дерева RB определен следующим образом:
struct _Rb_tree_node_base
{
typedef _Rb_tree_node_base* _Base_ptr;
typedef const _Rb_tree_node_base* _Const_Base_ptr;
_Rb_tree_color _M_color;
_Base_ptr _M_parent;
_Base_ptr _M_left;
_Base_ptr _M_right;
...
Там вы можете наблюдать эти 3 указателя.
Как правило, трудно сказать, какой контейнер будет занимать меньше общей памяти.Однако я создал эталонный тест для вставки случайных чисел по 1M в оба контейнера и измерил максимальный размер резидента (MaxRSS), чтобы отразить все использованное пространство памяти, включая, например, данные обслуживания кучи.Результаты были следующими:
- 48,344 кБ для
std::map
, - 50 888 кБ для
std::unordered_map
, - 40,932 кБ для
std::unordered_map
с reserve
.
Обратите внимание, что потребление памяти для неупорядоченной карты (объявление 2) было выше из-за перераспределениясписки ведра.Для этого и предназначена reserve
функция-член.Если кто-то заботится о потреблении памяти и заранее знает количество элементов, он / она должен всегда выделять заранее (это та же самая ситуация, что и для векторов).