QVector проти QList


80

У мене є список цілих чисел, які мені потрібно повторити, але масив неадекватний. У чому різниця між vectorsта listsі чи є щось, що мені потрібно знати, перш ніж вибрати тип?

Щоб зрозуміти, я прочитав документи QT, але це те, що я знаю:

QList<T>,, QLinkedList<T>та QVector<T>забезпечують подібну функціональність. Ось огляд:

  • Для більшості цілей QListце правильний клас для використання. Його API на основі індексу зручніший, ніж QLinkedList'sAPI на основі ітераторів, і, як правило, він швидший, ніж QVectorчерез те, як він зберігає свої елементи в пам'яті. Він також розширюється до меншого коду у вашому виконуваному файлі.
  • Якщо вам потрібен справжній зв’язаний список із гарантіями постійної вставки часу в середині списку та ітераторів до елементів, а не до індексів, використовуйте QLinkedList.
  • Якщо ви хочете, щоб елементи займали сусідні позиції пам'яті, використовуйте QVector.

Відповіді:


122

QVectorв основному є аналогом std::vector, як можна здогадатися з назви. QListближче до boost::ptr_deque, незважаючи на очевидну асоціацію з std::list. Він не зберігає об’єкти безпосередньо, а натомість зберігає вказівники на них. Ви отримуєте всі переваги швидкої вставки на обох кінцях, а перерозподіл передбачає перемішування покажчиків замість конструкторів копіювання, але ви втрачаєте просторову локальність фактичного std::dequeабо std::vector, і отримуєте багато розподілу купи. Він дійсно приймає рішення, щоб уникнути розподілу купи для невеликих об’єктів, повернення просторової місцевості, але, наскільки я розумію, це стосується лише речей, менших заint .

QLinkedList є аналогом std::list і має всі його мінуси. Взагалі кажучи, це повинен бути ваш останній вибір контейнера.

Бібліотека QT надає перевагу використанню QListоб'єктів, тому, надаючи їм перевагу у власному коді, іноді можна уникнути непотрібних сум. Додаткове використання купи і випадкове розташування фактичних даних можуть теоретично зашкодити за певних обставин, але часто це непомітно. Тому я б запропонував використовувати, QListпоки профілювання не запропонує змінити на QVector. Якщо ви очікуєте, що суміжне розподіл буде важливим [читайте: ви взаємодієте з кодом, який очікує T[]замість a QList<T>], що також може бути причиною для того, щоб розпочати QVectorзразу.


Якщо ви запитуєте про контейнери загалом і просто використовували документи QT як посилання, то вищевказана інформація менш корисна.

An std::vector- це масив, розмір якого можна змінити. Всі елементи зберігаються поруч, і ви можете швидко отримати доступ до окремих елементів. Недоліком є ​​те, що вставки ефективні лише з одного кінця. Якщо ви покладете щось посередині або на початку, вам доведеться скопіювати інші предмети, щоб звільнити місце. У нотації великих ой вставка в кінці - 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 має досить добрі гарантії щодо посилань. Ітератори легко анулювати, але вказівники на членів досить надійні для операцій з деквіей.
Бен,

Чудова відповідь. Але у мене є додаткове запитання: що швидше повторити? У мене є набір об’єктів, вони насправді не часто вставляються або видаляються, але мені потрібно переглядати об’єкти якомога швидше. Що швидше?
birgersp

Хороша інформація. Я знайшов наступне твердження найбільш корисним ... "Бібліотека QT сильно сприяє використанню об'єктів QList". У моєму випадку використання я маю справу з QTableWidget, який любить QList і QListString. Тому нехай ваш варіант використання диктує ваше рішення між QVector та QList.
panofish

1
Ви acually протестовані std::dequeпроти std::vector? Ви будете здивовані ...
Марк Мутц - ммюц 02.03.16

4
Будь ласка, майте на увазі, що документація була оновлена ​​після скарг розробників Qt, що QList є поганою рекомендацією як контейнер за замовчуванням. Тепер у ньому зазначено: " Першим вибором за замовчуванням повинен бути QVector . [...] Однак QList використовується в усіх API Qt для передачі параметрів і для повернення значень. Використовуйте QList для взаємодії з цими API. " (Документація QList)
AntonyG

63

Справа змінилася

Зараз ми знаходимося в Qt 5.8, і все змінилося, тому документація. Це дає чітку і різну відповідь на це питання:

QVectorмає бути першим вибором за замовчуванням. QVector<T>зазвичай дає кращу продуктивність, ніж QList<T>, тому що QVector<T>завжди зберігає свої елементи послідовно в пам'яті, де QList<T>буде розподіляти свої елементи в купі, якщо sizeof(T) <= sizeof(void*)і T не оголошено як a Q_MOVABLE_TYPEабо як Q_PRIMITIVE_TYPEвикористання Q_DECLARE_TYPEINFO.

QListПояснення див. У плюсах і мінусах використання . Однак QListвикористовується в Qt API для передачі параметрів і для повернення значень. Використовуйте QListдля взаємодії з цими API.


2
Це повинно зростати, і тепер це буде прийнято як відповідь.
ymoreau

12

В QVectorподібний до std::vector. QLinkedListє подібним до std::list. QListє вектором на основі індексу, але позиція пам’яті не гарантована (наприклад std::deque).


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.