Я заметил в Effective STL, что
вектор - это тип последовательности, который должен использоваться по умолчанию.
Что это значит? Кажется, что игнорировать эффективность vector
может все что угодно.
Может ли кто-нибудь предложить мне сценарий, в котором vector
вариант невозможен, но list
должен использоваться?
Ответы:
Ситуации, когда вы хотите вставить много элементов в любое место, кроме конца последовательности несколько раз.
Проверьте гарантии сложности для каждого отдельного типа контейнера:
Каковы гарантии сложности стандартных контейнеров?
источник
list
естьpush_front
вектор:
список:
В общем, используйте vector, когда вам все равно, какой тип последовательного контейнера вы используете, но если вы делаете много вставок или стираний в и из любого места контейнера, кроме конца, вам понадобится использовать список. Или, если вам нужен произвольный доступ, вам нужен вектор, а не список. Кроме этого, есть естественные случаи, когда вам понадобится то или иное на основе вашего приложения, но в целом это хорошие рекомендации.
источник
reserve()
чтобы уменьшить это до O (1). Добавление новых элементов в список (т. Е. Не объединение их) выполняет O (n) свободных распределений хранилища.list
освобождает память, когда вы стираете элементы, ноvector
не освобождает . Аvector
не уменьшает свою емкость, когда вы уменьшаете его размер, если вы не используетеswap()
хитрость.Если вам не нужно часто вставлять элементы, вектор будет более эффективным. Он имеет гораздо лучшую локальность кэша процессора, чем список. Другими слова, доступ к одному элементу делает это очень вероятно , что следующий элемент присутствует в кэше и может быть получен без чтения медленной памяти.
источник
Большинство ответов здесь пропускают одну важную деталь: зачем?
Что вы хотите сохранить в контейнере?
Если это набор
int
s, тоstd::list
он проиграет в каждом сценарии, независимо от того, сможете ли вы перераспределить, вы удалите его только с фронта и т. Д. Списки перемещаются медленнее, каждая вставка обходится вам взаимодействием с распределителем. Было бы крайне сложно подготовить пример, гдеlist<int>
бьетvector<int>
. И даже тогда,deque<int>
может быть лучше или близко, не просто использование списков, которые будут иметь большие накладные расходы памяти.Тем не менее, если вы имеете дело с большими, уродливыми сгустками данных - и немногими из них - вы не хотите перераспределять при вставке, а копирование из-за перераспределения было бы катастрофой - тогда вам, возможно, будет лучше с
list<UglyBlob>
чемvector<UglyBlob>
.Тем не менее, если вы переключитесь на
vector<UglyBlob*>
или дажеvector<shared_ptr<UglyBlob> >
, снова - список будет отставать.Таким образом, схема доступа, количество целевых элементов и т. Д. Все еще влияет на сравнение, но, на мой взгляд, размер элементов - стоимость копирования и т. Д.
источник
list<T>
является возможностьsplice
в O (1) . Если вам нужно постоянное соединение, список также может быть структурой выбора;)UglyBlob
- даже объекты с несколькими строковыми членами могут быть слишком дорогими для копирования, поэтому перераспределение будет стоить. Также: не пренебрегайте пространственными накладными расходами, которыеvector
могут вызвать экспоненциальный рост удерживающих объектов размером в несколько десятков байт (если вы не можетеreserve
заранее).vector<smart_ptr<Large>>
противlist<Large>
- я бы сказал, если вам нужен произвольный доступ к элементам, этоvector
имеет смысл. Если вам не нужен произвольный доступ, то онlist
кажется более простым и должен работать одинаково.Одной из специальных возможностей std :: list является объединение (связывание или перемещение части или целого списка в другой список).
Или, возможно, если ваш контент очень дорогой для копирования. В таком случае может быть дешевле, например, отсортировать коллекцию со списком.
Также обратите внимание, что если коллекция небольшая (а ее содержимое не особенно дорого копировать), вектор все равно может превзойти список, даже если вы вставляете и удаляете его где угодно. Список распределяет каждый узел по отдельности, и это может быть намного дороже, чем перемещение нескольких простых объектов.
Я не думаю, что есть очень жесткие правила. Это зависит от того, что вы в основном хотите сделать с контейнером, а также от того, какой большой размер вы ожидаете от контейнера, и от типа содержимого. Вектор обычно превосходит список, потому что он выделяет свое содержимое как один непрерывный блок (это в основном динамически размещаемый массив, и в большинстве случаев массив является наиболее эффективным способом хранения множества вещей).
источник
list::size
не обязательно постоянное время. См stackoverflow.com/questions/228908/is-listsize-really-on и gcc.gnu.org/ml/libstdc++/2005-11/msg00219.htmlsplice
между различными списками задается как линейная сложность иsize
является конкретно постоянной. Таким образом, чтобы приспособиться к новичкам, которые выполняют итерацииsize
или что-то еще, целый класс алгоритмов недосягаем для STL или любого периода «совместимых» контейнеров.Что ж, ученики моего класса, кажется, совершенно не в состоянии объяснить мне, когда более эффективно использовать векторы, но они выглядят весьма счастливыми, советуя мне использовать списки.
Вот как я это понимаю
Списки : каждый элемент содержит адрес следующего или предыдущего элемента, поэтому с помощью этой функции вы можете рандомизировать элементы, даже если они не отсортированы, порядок не изменится: это эффективно, если ваша память фрагментирована. Но у него есть и другое очень большое преимущество: вы можете легко вставлять / удалять элементы, потому что единственное, что вам нужно сделать, это изменить некоторые указатели. Недостаток: чтобы прочитать случайный элемент, вы должны переходить от одного элемента к другому, пока не найдете правильный адрес.
Векторы : при использовании векторов память намного более организована, как обычные массивы: каждый n-й элемент сохраняется сразу после (n-1) -го элемента и до (n + 1) -го элемента. Почему это лучше, чем список? Потому что это позволяет быстрый произвольный доступ. Вот как: если вы знаете размер элемента в векторе, и если они находятся в памяти рядом, вы можете легко предсказать, где находится n-й элемент; вам не нужно просматривать весь элемент списка, чтобы прочитать тот, который вы хотите, с вектором, вы непосредственно читаете его, со списком, который вы не можете. С другой стороны, изменить векторный массив или изменить значение гораздо медленнее.
Списки больше подходят для отслеживания объектов, которые могут быть добавлены / удалены в памяти. Векторы больше подходят, когда вы хотите получить доступ к элементу из большого количества отдельных элементов.
Я не знаю, как оптимизируются списки, но вы должны знать, что если вы хотите быстрый доступ для чтения, вы должны использовать векторы, потому что, как бы хорошо STL ни закреплял списки, в доступе для чтения он будет не таким быстрым, как векторный.
источник
vector
вызвано изменением его размера может быть медленным? затем согласились, но в тех случаях, когдаreserve()
их можно использовать, это позволяет избежать этих проблем.В основном вектор - это массив с автоматическим управлением памятью. Данные непрерывны в памяти. Попытка вставить данные посередине - дорогостоящая операция.
В списке данные хранятся в несвязанных местах памяти. Вставка посередине не требует копирования некоторых данных, чтобы освободить место для новой.
Чтобы ответить более конкретно на ваш вопрос, я процитирую эту страницу
источник
В любое время вы не можете сделать итераторы недействительными.
источник
deque
.Когда у вас много вставки или удаления в середине последовательности. например менеджер памяти.
источник
Сохранение достоверности итераторов является одной из причин использования списка. Другой случай, когда вы не хотите, чтобы вектор перераспределялся при нажатии элементов. Это может управляться с помощью разумного использования Reserve (), но в некоторых случаях может быть проще или более целесообразно просто использовать список.
источник
Когда вы хотите перемещать объекты между контейнерами, вы можете использовать
list::splice
.Например, алгоритм разбиения графа может иметь постоянное количество объектов, рекурсивно разделенных между увеличивающимся числом контейнеров. Объекты должны быть инициализированы один раз и всегда оставаться в тех же местах в памяти. Гораздо быстрее переставить их путем перекомпоновки, чем перераспределением.
Редактировать: поскольку библиотеки готовятся к реализации C ++ 0x, общий случай объединения подпоследовательности в список становится линейной сложностью с длиной последовательности. Это связано с тем, что
splice
(сейчас) необходимо выполнить итерацию последовательности для подсчета количества элементов в ней. (Поскольку список должен записывать его размер.) Простой подсчет и повторное связывание списка все еще быстрее, чем любая альтернатива, и объединение всего списка или одного элемента - это особые случаи с постоянной сложностью. Но если у вас есть длинные последовательности для соединения, вам, возможно, придется покопаться в поисках лучшего, старомодного, несовместимого контейнера.источник
Единственное жесткое правило, где
list
необходимо использовать, - это когда вам нужно распределить указатели на элементы контейнера.В отличие от
vector
, вы знаете, что память элементов не будет перераспределена. Если это так, то у вас могут быть указатели на неиспользуемую память, которая в лучшем случае является большой нет-нет, а в худшемSEGFAULT
.(Технически a
vector
of*_ptr
также будет работать, но в этом случае вы эмулируете,list
так что это просто семантика.)Другие мягкие правила связаны с возможными проблемами производительности при вставке элементов в середину контейнера, в
list
этом случае предпочтительнее.источник
Сделайте это просто -
В конце дня, когда вы не уверены, выбирая контейнеры в C ++, используйте это изображение блок-схемы (скажите спасибо): - введите описание изображения здесь
Вектор
1. Вектор основан на заразной память
2. вектора путь для небольшого набора данных
3. вектор выполнять быстрый при обходе по данным множественного
4. вектора вставки делеции медленно на огромном наборе данных , но быстро для очень небольших
List-
1. Список основан на динамической памяти
2. Список путь для очень огромного набора данных
3. Список сравнительно медленно на обходе небольшой набор данных , но быстро на огромный набор данных
4. список вставки делеции быстро на огромный набор данных, но медленный на меньших
источник
Списки - это просто оболочка для двунаправленного LinkedList в stl, таким образом, предлагая функцию, которую вы можете ожидать от d-linklist, а именно вставку и удаление O (1). В то время как векторы представляют собой заразную последовательность данных, которая работает как динамический массив. PS - проще пересекать.
источник
В случае вектора и списка основные отличия, которые меня выделяют, следующие:
вектор
Вектор хранит свои элементы в смежной памяти. Следовательно, произвольный доступ возможен внутри вектора, что означает, что доступ к элементу вектора очень быстрый, потому что мы можем просто умножить базовый адрес на индекс элемента для доступа к этому элементу. Фактически, для этой цели требуется только O (1) или постоянное время.
Поскольку вектор в основном оборачивает массив, каждый раз, когда вы вставляете элемент в вектор (динамический массив), он должен изменять свой размер, находя новый непрерывный блок памяти для размещения новых элементов, что требует больших затрат времени.
Он не потребляет дополнительной памяти для хранения указателей на другие элементы внутри него.
список
Список хранит свои элементы в несмежной памяти. Следовательно, произвольный доступ невозможен внутри списка, что означает, что для доступа к его элементам мы должны использовать указатели и проходить по списку, который медленнее, чем вектор. Это занимает O (n) или линейное время, которое медленнее, чем O (1).
Поскольку в списке используется несмежная память, время, затрачиваемое на вставку элемента в список, намного эффективнее, чем в случае его векторного аналога, поскольку перераспределение памяти исключается.
Он потребляет дополнительную память для хранения указателей на элемент до и после определенного элемента.
Таким образом, учитывая эти различия, мы обычно учитываем память , частый произвольный доступ и вставку, чтобы определить победителя списка векторов в данном сценарии.
источник
Список является двусвязным списком, поэтому его легко вставить и удалить. Мы должны просто изменить несколько указателей, тогда как в векторе, если мы хотим вставить элемент посередине, каждый элемент после него должен сдвигаться на один индекс. Также, если размер вектора заполнен, он должен сначала увеличить его размер. Так что это дорогая операция. Таким образом, везде, где операции вставки и удаления должны выполняться чаще, в таком случае следует использовать список.
источник