QVector против QList

80

У меня есть список целых чисел, которые мне нужно перебрать, но массив неадекватен. В чем разница между vectorsи listsесть ли что-нибудь, что мне нужно знать, прежде чем я выберу тип?

Чтобы быть ясным, я читал документы QT, но это то, что я знаю:

QList<T>,, QLinkedList<T>и QVector<T>предоставляют аналогичные функции. Вот обзор:

  • Для большинства целей QListэто подходящий класс. Его API на основе индексов удобнее, чем QLinkedList'sAPI на основе итераторов, и обычно он быстрее, чем QVectorиз-за способа хранения элементов в памяти. Он также расширяется до меньшего количества кода в вашем исполняемом файле.
  • Если вам нужен настоящий связанный список с гарантиями постоянного времени вставки в середине списка и итераторами для элементов, а не индексов, используйте QLinkedList.
  • Если вы хотите, чтобы элементы занимали соседние позиции памяти, используйте QVector.
Jcuenod
источник

Ответы:

122

QVectorв основном аналогичен std::vector, как можно догадаться по названию. QListближе к boost::ptr_deque, несмотря на очевидную связь с std::list. Он не хранит объекты напрямую, а вместо этого хранит указатели на них. Вы получаете все преимущества быстрой вставки на обоих концах, а перераспределение включает перетасовку указателей вместо конструкторов копирования, но теряет пространственную локальность фактического std::dequeor std::vectorи получает много выделения кучи. У него есть некоторые решения, чтобы избежать выделения кучи для небольших объектов, восстанавливая пространственную локальность, но, насколько я понимаю, это применимо только к вещам, меньшим, чем int.

QLinkedListаналогичен ему std::listи имеет все его недостатки. Вообще говоря, это должен быть ваш последний выбор контейнера.

Библиотека QT сильно поддерживает использование QListобъектов, поэтому использование их в собственном коде иногда может избежать ненужной утомительности. Дополнительное использование кучи и случайное расположение фактических данных теоретически может повредить при некоторых обстоятельствах, но часто незаметно. Поэтому я бы предложил использовать, QListпока профилирование не предложит перейти на QVector. Если вы ожидаете, что непрерывное распределение будет важным [прочтите: вы взаимодействуете с кодом, который ожидает T[]вместо a QList<T>], это также может быть причиной для начала QVectorсразу же.


Если вы спрашиваете о контейнерах в целом и просто использовали документы QT в качестве справки, то приведенная выше информация менее полезна.

An std::vector- это массив, размер которого можно изменять. Все элементы хранятся рядом друг с другом, и вы можете быстро получить доступ к отдельным элементам. Обратной стороной является то, что вставки эффективны только на одном конце. Если вы поместите что-то посередине или в начале, вам придется скопировать другие объекты, чтобы освободить место. В нотации big-oh вставка в конце - это O (1), вставка где-либо еще - O (N), а произвольный доступ - O (1).

An std::dequeаналогичен, но не гарантирует, что объекты хранятся рядом друг с другом, и позволяет вставлять на обоих концах O (1). Это также требует одновременного выделения меньших фрагментов памяти, что иногда может быть важно. Произвольный доступ - это O (1), а вставка в середине - O (N), как и для a vector. Пространственная локальность хуже std::vector, но объекты имеют тенденцию группироваться, так что вы получаете некоторые преимущества.

Это std::listсвязанный список. Он требует наибольших затрат памяти из трех стандартных последовательных контейнеров, но предлагает быструю вставку в любом месте ... при условии, что вы заранее знаете, куда вам нужно вставить. Он не предлагает произвольный доступ к отдельным элементам, поэтому вам нужно выполнить итерацию за O (N). Но как только это произойдет, фактическая вставка будет O (1). Самым большим преимуществом std::listявляется то, что вы можете быстро их объединить ... если вы переместите весь диапазон значений в другой std::list, вся операция будет O (1). Также намного сложнее сделать ссылки в списке недействительными, что иногда может быть важно.

Как правило, я предпочитаю это std::dequeделать std::vector, если мне не нужно передавать данные в библиотеку, которая ожидает необработанный массив. std::vectorгарантируется непрерывность, поэтому &v[0]работает для этой цели. Я не помню, когда в последний раз использовал a std::list, но это было почти наверняка потому, что мне требовались более строгие гарантии относительно того, что ссылки остаются в силе.

Деннис Зикефуз
источник
FWIW, std :: deque дает довольно хорошие гарантии относительно ссылок. Итераторы легко сделать недействительными, но указатели на члены довольно устойчивы к операциям с удалением из очереди.
Ben
Отличный ответ. Но у меня есть дополнительный вопрос: что быстрее повторять? У меня есть набор объектов, они на самом деле не часто вставляются или удаляются, но мне нужно как можно быстрее перебирать объекты. Что быстрее?
birgersp
Хорошая информация. Наиболее полезным мне показалось следующее утверждение ... «Библиотека QT сильно поддерживает использование объектов QList». В моем случае использования я имею дело с QTableWidget, которому нравятся QList и QListString. Поэтому позвольте вашему варианту использования определять ваше решение между QVector и QList.
panofish
1
Вы acually протестированные std::dequeпротив std::vector? Вы будете удивлены ...
Марк Мутц - mmutz
4
Имейте в виду, что документация была обновлена ​​после жалоб разработчиков Qt на то, что QList - плохая рекомендация в качестве контейнера по умолчанию. Теперь в нем говорится: « QVector должен быть вашим первым выбором по умолчанию. [...] Однако QList используется во всех API Qt для передачи параметров и для возврата значений. Используйте QList для взаимодействия с этими API. » (Документация QList)
AntonyG
64

Времена изменились

Сейчас мы находимся в Qt 5.8, и все изменилось, поэтому документация. Он дает четкий и иной ответ на этот вопрос:

QVector должен быть вашим первым выбором по умолчанию. QVector<T>обычно дает лучшую производительность, чем QList<T>, потому что QVector<T>всегда сохраняет свои элементы последовательно в памяти, где QList<T>будет размещать свои элементы в куче, если sizeof(T) <= sizeof(void*)и T не был объявлен как a Q_MOVABLE_TYPEили a Q_PRIMITIVE_TYPEusing Q_DECLARE_TYPEINFO .

См. QListОбъяснения в разделе «За и против» использования . Тем не мение,QList он используется во всех API Qt для передачи параметров и для возврата значений. Используйте QListдля взаимодействия с этими API.

Длевин
источник
2
Это должно возрасти, и теперь это должно быть принято как ответ.
ymoreau
12

В QVectorпохож на std::vector. QLinkedListпохоже на std::list. QListвектор на основе индекса, но позиция в памяти не гарантируется (как std::deque).

Naszta
источник
3

Из документа QtList:

  • QList будет использоваться в большинстве случаев. Для структур с тысячей элементов обеспечивает эффективную вставку в середине и обеспечивает индексированный доступ. prepend()и append()очень быстро, поскольку память предварительно выделена на обоих концах внутреннего массива. QList<T>является массивом указателя типа T. Если T имеет указатель или тип общего указателя Qt, объект сохраняется непосредственно в массиве

  • QVectorпредпочтительнее в случае большого количества append()или insert()новых элементов с размером больше указателя, так как QVectorпамять для своих элементов выделяется в единой куче. Для QListвставки или добавления нового элемента требуется выделение памяти для нового элемента в куче. Короче говоря, если вы хотите, чтобы элементы занимали соседние позиции в памяти или если ваши элементы больше указателя, и вы хотите избежать накладных расходов на выделение их в куче по отдельности во время вставки, используйте QVector.

кирилов
источник
-2

QVector похож на массив, который может изменять размер (увеличиваться или уменьшаться), но требует больших транзакций, вычислений и времени.

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

Тем не мение, QLinkedList работает с указателями. Таким образом, когда создается новый элемент, выделяется только новое пространство памяти и связывается с единственным фрагментом памяти. Поскольку он работает с указателями, он работает быстрее и эффективнее.

Если у вас есть список элементов, размер которых вы не ожидаете сильно изменить, QVectorэто, вероятно, хорошо, но обычно QLinkedListиспользуется для большинства целей.

Ник
источник
3
-1: QVector не идет с тяжелыми транзакциями, как вы утверждаете, потому что он предварительно выделяет и имеет хорошую стратегию увеличения / уменьшения для добавления и извлечения. Добавление / вставка действительно требует перемещения диапазонов данных, но поскольку они непрерывны в памяти, это делается очень быстро (кеш может хорошо работать с непрерывными данными, в отличие от разрозненных кусков кучи, как в QList). Ваше второе утверждение о том, что QLinkedList используется для большинства целей, совершенно неверно. Применяется крайне редко. Вы можете спутать его с QList?
DerManu