Вектор против списка в STL

Я заметил в Effective STL, что

вектор - это тип последовательности, которая должен использоваться по умолчанию.

Что это значит? Кажется, что игнорировать эффективность vector может что-то сделать.

Может ли кто-нибудь предложить мне сценарий, где vector не является допустимым вариантом, но list должен использоваться?

Ответ 2

вектор

  • Непрерывная память.
  • Предварительно выделяет пространство для будущих элементов, поэтому дополнительное пространство требуется за пределами того, что необходимо для самих элементов.
  • Каждому элементу требуется только пространство для самого типа элемента (без дополнительных указателей).
  • Можно повторно назначить память для всего вектора в любое время, когда вы добавите элемент.
  • Вставки в конце являются постоянными, амортизируются, но вставки в другом месте являются дорогостоящими O (n).
  • Стирания в конце вектора являются постоянным временем, но для остальных это O (n).
  • Вы можете случайно получить доступ к своим элементам.
  • Итераторы недействительны, если вы добавляете или удаляете элементы в вектор или из него.
  • Вы можете легко получить базовый массив, если вам нужен массив элементов.

список:

  • Непрерывная память.
  • Нет предварительно выделенной памяти. Накладные расходы памяти для самого списка являются постоянными.
  • Каждому элементу требуется дополнительное пространство для node, которое содержит элемент, включая указатели на следующий и предыдущие элементы в списке.
  • Никогда не нужно перераспределять память для всего списка только потому, что вы добавляете элемент.
  • Вставки и стирания дешевы независимо от того, где они находятся в списке.
  • Это дешево сочетать списки со сплайсингом.
  • Вы не можете случайным образом обращаться к элементам, поэтому получение определенного элемента в списке может быть дорогостоящим.
  • Итераторы остаются в силе даже при добавлении или удалении элементов из списка.
  • Если вам нужен массив элементов, вам нужно будет создать новый и добавить к нему все, так как нет базового массива.

В общем, используйте вектор, когда вам не нужен какой тип последовательного контейнера, который вы используете, но если вы делаете много вложений или стираний в любом другом месте контейнера, кроме конца, вы собирается использовать список. Или, если вам нужен произвольный доступ, тогда вам понадобится вектор, а не список. Помимо этого, есть естественные случаи, когда вам понадобится тот или иной, основанный на вашем приложении, но в целом это хорошие рекомендации.

Ответ 3

Если вам не нужно часто вставлять элементы, тогда вектор будет более эффективным. Он имеет гораздо лучшую локальность кэша процессора, чем список. Другими словами, доступ к одному элементу делает очень вероятным, что следующий элемент присутствует в кеше и может быть извлечен без необходимости чтения медленной ОЗУ.

Ответ 4

В большинстве ответов здесь не хватает одной важной детали: зачем?

Что вы хотите сохранить в контейнере?

Если это набор int, то std::list проиграет в каждом сценарии, независимо от того, сможете ли вы перераспределить, вы удалите его только с фронта и т.д. Списки перемещаются медленнее, каждая вставка обходится вам взаимодействием с распределитель. Было бы крайне сложно подготовить пример, в котором list<int> бьет vector<int>. И даже в этом случае deque<int> может быть лучше или близким, не просто используя списки, которые будут иметь большую нагрузку на память.

Тем не менее, если вы имеете дело с большими, уродливыми каплями данных - и немногими из них - вы не хотите перераспределять при вставке, а копирование из-за перераспределения будет катастрофой - тогда вам, возможно, будет лучше с list<UglyBlob>, чем vector<UglyBlob>.

Тем не менее, если вы переключитесь на vector<UglyBlob*> или даже vector<shared_ptr<UglyBlob> >, снова - список будет отставать.

Таким образом, схема доступа, количество целевых элементов и т.д. Все еще влияет на сравнение, но, на мой взгляд, размер элементов - стоимость копирования и т.д.

Ответ 5

Одной из специальных возможностей std:: list является сращивание (связывание или перемещение части или всего списка в другой список).

Или, возможно, если ваше содержимое очень дорого копировать. В таком случае может быть дешевле, например, сортировать коллекцию со списком.

Также обратите внимание, что если коллекция небольшая (и содержимое не особенно дорого копируется), вектор может все же превосходить список, даже если вы вставляете и стираете в любом месте. Список выделяет каждый node отдельно, и это может быть намного дороже, чем перемещение нескольких простых объектов.

Я не думаю, что есть очень жесткие правила. Это зависит от того, что вы в основном хотите делать с контейнером, а также от того, насколько велики вы ожидаете наличие контейнера и тип содержимого. Вектор обычно перехватывает список, поскольку он выделяет его содержимое как единый непрерывный блок (в основном это динамически распределенный массив, и в большинстве случаев массив является наиболее эффективным способом для хранения кучи вещей).

Ответ 6

Ну, ученики моего класса, похоже, не могут объяснить мне, когда эффективнее использовать векторы, но они выглядят вполне счастливыми, советуя мне использовать списки.

Вот как я это понимаю

Списки: Каждый элемент содержит адрес следующего или предыдущего элемента, поэтому с помощью этой функции вы можете рандомизировать элементы, даже если они не отсортированы, порядок не изменится: он эффективен, если память фрагментирована. Но у этого есть и другое очень большое преимущество: вы можете легко вставлять/удалять элементы, потому что единственное, что вам нужно сделать, это изменить некоторые указатели. Минус: Чтобы прочитать случайный отдельный элемент, вам нужно перейти от одного элемента к другому, пока не найдете правильный адрес.

Векторы: При использовании векторов память гораздо более организована, как обычные массивы: каждый n-й элемент хранится сразу после (n-1) -го элемента и до (n + 1) -го элемента. Почему это лучше, чем список? Потому что он обеспечивает быстрый произвольный доступ. Вот как это сделать: если вы знаете размер элемента в векторе и если они смежны в памяти, вы можете легко предсказать, где находится n-й элемент; вам не нужно просматривать весь элемент списка, чтобы прочитать тот, который вы хотите, с вектором, вы его прямо читаете, а список невозможен. С другой стороны, изменить векторный массив или изменить значение намного медленнее.

Списки более подходят для отслеживания объектов, которые могут быть добавлены/удалены в памяти. Векторы более подходят, если вы хотите получить доступ к элементу из большого количества отдельных элементов.

Я не знаю, как оптимизируются списки, но вы должны знать, что если вы хотите быстрый доступ для чтения, вы должны использовать векторы, потому что, насколько хорош список STL, он будет не так быстро в режиме чтения чем вектор.

Ответ 7

В основном вектор представляет собой массив с автоматическим управлением памятью. Данные смежны в памяти. Попытка вставить данные в середине - это дорогостоящая операция.

В списке данные хранятся в не связанных ячейках памяти. Вставка в середине не требует копирования некоторых данных, чтобы освободить место для нового.

Чтобы более точно ответить на ваш вопрос, я приведу эту страницу

векторы, как правило, наиболее эффективны во времени для доступа к элементам и для добавления или удаления элементов из конца последовательности. Для операций, связанных с вставкой или удалением элементов в позициях, отличных от конца, они выполняют хуже, чем знаки и списки, и имеют менее согласованные итераторы и ссылки, чем списки.

Ответ 8

Если вы не можете сделать итераторы недействительными.

Ответ 9

Если у вас много вставки или удаления в середине последовательности. например диспетчер памяти.

Ответ 10

Сохранение достоверности итераторов является одной из причин использования списка. Другое дело, когда вы не хотите, чтобы вектор перераспределялся при нажатии элементов. Это может управляться интеллектуальным использованием reserve(), но в некоторых случаях было бы проще или более просто использовать список.

Ответ 11

Если вы хотите перемещать объекты между контейнерами, вы можете использовать list::splice.

Например, алгоритм разбиения графа может иметь постоянное количество объектов, рекурсивно разделенных между растущим числом контейнеров. Объекты должны быть инициализированы один раз и всегда оставаться в тех же местах в памяти. Это намного быстрее, чтобы переставить их, перейдя, чем путем перераспределения.

Изменить:, поскольку библиотеки готовятся к реализации С++ 0x, общий случай сплайсинга подпоследовательности в списке становится линейной сложностью с длиной последовательности. Это связано с тем, что splice (сейчас) необходимо перебрать последовательность, чтобы подсчитать количество элементов в ней. (Поскольку список должен записывать свой размер.) Простое подсчет и переустановка списка по-прежнему быстрее, чем любая альтернатива, а сплайсинг всего списка или одного элемента - это особые случаи с постоянной сложностью. Но, если у вас есть длинные последовательности для сращивания, вам, возможно, придется копаться в поисках лучшего, старомодного, несоответствующего контейнера.

Ответ 12

Единственное жесткое правило, в котором следует использовать list, - это то, где вам нужно распространять указатели на элементы контейнера.

В отличие от vector, вы знаете, что память элементов не будет перераспределена. Если бы это могло быть, тогда у вас могли бы быть указатели на неиспользуемую память, что в лучшем случае большое, не-нет и в худшем случае SEGFAULT.

(Технически a vector of *_ptr также будет работать, но в этом случае вы эмулируете list, чтобы просто семантика.)

Другие мягкие правила связаны с возможными проблемами производительности вставки элементов в середину контейнера, после чего предпочтительнее list.

Ответ 13

Списки - это просто оболочка для двунаправленного LinkedList в stl, таким образом, предлагая функцию, которую вы можете ожидать от d-linklist, а именно вставку и удаление O (1). В то время как векторы представляют собой заразительную последовательность данных, которая работает как динамический массив. P. S- легче пройти.