Коли слід використовувати вектор / список?


17

Я можу зрозуміти, коли використовувати списки, але я не розумію, коли краще використовувати вектори, ніж використовувати списки у відеоіграх: коли краще мати швидкий випадковий доступ?

(І я розумію, чому швидше вставляти / видаляти в списки, оскільки він просто видаляє / додає покажчики, але він все одно повинен знайти відповідний елемент ...)


Повторно доданий векторний тег до цього - якщо список є дійсним тегом, то вектор також.
Килотан

Імовірно, вектор був видалений, тому що він мається на увазі математичний вектор, а не std :: вектор.

1
як щодо nix їх обох і поставити container.
deft_code

@Kylotan: Це так, як сказав Джо. Це питання, безумовно, стосувалося векторів, але воно не належало до тегу вектора.
doppelgreener

1
Тож чи видаляємо ми будь-який неоднозначний тег? Це звучить як неправильне рішення для мене - краще, щоб ваш пошук виявляв занадто багато інформації, ніж недостатньо. Пропустити небажані результати простіше, ніж мозковий штурм, щоб знайти синоніми.
Килотан

Відповіді:


29

Моє правило, і я впевнений, що з цього приводу будуть дискусії, ніколи не використовувати списки (якщо тільки вам не потрібно дуже і дуже часто видаляти речі з середини великих списків).

Швидкість, яку ви отримаєте, зберігаючи всі елементи в контейнері в суміжній пам'яті (і, отже, більш зручній для кешу), варто компенсувати додаткові витрати на додавання / вилучення / зміну розміру вектора.

Редагувати: Для того, щоб уточнити трохи більше, звичайно, не слід говорити про те, що будь-яке "швидше" питання слід перевіряти на будь-якій платформі з будь-якими наборами даних, що відповідають вашим конкретним потребам. Якщо мені просто потрібна колекція елементів, я просто використовую вектор (або декор, що майже те саме), якщо немає вагомих причин цього не робити.


1
Я думаю, що це дуже залежить від ваших потреб, якщо вам ніколи не потрібно отримувати доступ до певного елемента, а вам просто потрібно прочитати їх, і ви часто додаєте та видаляєте елементи, список є кращим рішенням.
Фредерік Імбео

11
@ Frédérick: Це стандартна C ++ мудрість, але вона також майже завжди помиляється. Вектори, особливо коли ми маємо справу з векторами покажчиків (які ви майже завжди займаєтеся іграми), надзвичайно швидко видаляють речі з середини - це лінійний час, але це дуже невеликі накладні витрати на предмет. Це також набагато швидше повторювати послідовно над вектором.

1
Я не впевнений, яку саме структуру ви отримуєте - такий оптимізація потребує конкретних прикладів, щоб сказати щось остаточно. Наприклад, моєю першою реакцією на випадки використання буде невпорядкований набір, який дозволяє однаково швидко вставляти, видаляти та шукати; на мій досвід, набори чудово підходять для об'єктів у редакторах. Але оскільки редактори мають значно менші вимоги до продуктивності в режимі реального часу - наприклад, це добре, якщо відповідь на натискання кнопки «Видалити» займає 1/20 секунди або займає 1/2 секунди 10% часу - такий рівень оптимізації також рідко стосується їх.

1
@ Frédérick Imbeault Modified - це не велика проблема. Додавання \ Видалення викликає проблеми. Від точки додавання \ видалення до кінця вектора копіюється для збереження вектору безперервно. Якщо порядок елементів не має значення, ви можете поміняти видалений елемент останнім елементом, а потім вимкніть його для видалення та додайте до кінця для додавання.
каменеметал

4
Звичайно, взяти адресу елемента у векторі та зберігати його не безпечно. Але в цілому ви навряд чи колись робите це, натомість віддаєте перевагу мати вектор покажчиків елементів та копіювати вказівник навколо (чи щось подібне).
Тетрад

8

Використовуйте список, коли вимкнення ітератора, спричинене зміною середини вашої структури даних, спричинить проблему, або вам потрібно буде сортувати елементи, щоб заміни та поп-трюк для швидких видалень середньої колекції не спрацювали, і у вас великий кількість середніх видалених колекцій.

Ви також можете розглянути можливість використання Deque. Він має схожі характеристики продуктивності з вектором, але не потребує вектору в суміжній пам'яті і є трохи більш гнучким.


+1 за те, що єдина особа, яка згадує деки - ви отримуєте безперервну пам’ять і переваги швидкості пошуку векторів, з швидким введенням / видаленням з обох кінців.

5

Ваш вибір повинен відображати ваші потреби. Всі елементи векторів безперервні в пам'яті, і списки мають вказівки на наступні / попередні елементи, тому кожен з них має свою перевагу / недоліки:

Списки:

  • Кожен елемент має 2 цілих числа, щоб вказати на попередні та наступні елементи, тому найчастіше це на 8 байт більше для кожного елемента у вашому списку
  • Вставка лінійна за часом: O (n)
  • Видалення - це постійна робота: O (1)
  • Доступ до елемента x лінійний за часом: O (n)

Вектори:

  • Потрібно менше пам'яті (немає вказівників на інші елементи, це простий математичний алгоритм)
  • Видалення лінійне за часом: O (n)
  • Доступ до елемента x є постійним: O (1) (Це тому, що елементи неперервні в пам'яті, тому це простий математичний вектор роботиPtr + (x * bytesOfTheType))
  • Вставка може бути в лінійний час, але найчастіше це постійна операція: O (1) (Це тому, що вектор у масиві, але завжди резервує в 2 рази більше, ніж ємність, коли масив заповнений, тому копія масиву не є частою)

Тому список краще, коли програмі потрібно часто додавати та видаляти елементи, але ніколи не отримувати доступ (або рідко отримувати доступ) до певного елемента без потреби інших. Вектор слід використовувати для кращого часу доступу, але не вистачає ефективності, коли потрібно видалити або додати елементи.

Перевірте цю публікацію в stackoverflow, вона представляє справді приємний графік з основними питаннями щодо ваших потреб, який приведе вас до конкретного контейнера залежно від ваших відповідей:

/programming/366432/extending-stdlist


3
Цей графік дійсно повинен починатися з вузла "Ви зберігаєте покажчики та менше тисячі? Ні -> вектор".

Так, можливо, ви маєте рацію, я не вважав, що тип, який зберігається, також слід аналізувати.
Фредерік Імбео

2

Зазвичай списки використовуються для таких структур, як черги, де багато операцій додавання та видалення. Приклад: постійно змінюється список об'єктів, які слід оновлювати. Сам список містить лише об'єкти на екрані і тому часто змінюється.

Вектори (або масиви) краще підходять для колекції, яка не так сильно змінюється, і де вам потрібен швидкий доступ до окремих предметів колекції. Приклад: карта-плитка, де потрібно шукати плитки за заданим індексом.

Думка тетрадів може бути правдивою, але це залежить від мови програмування, яка використовується. Я бачу, що ви позначили своє запитання c++, але я намагався дати відповідь, що не залежить від мови.


Добре, що я міг би також поставити C в нього, але таких контейнерів у C немає, але це над чим подумати: чи є якась STL-схожа тиблиця для C?
jokoon

У минулому я використовував glib ( library.gnome.org/devel/glib ), який реалізує ряд стандартних структур даних для C. Я ненавидів це, оскільки він зазвичай занадто багатослівний і відчайдушно хоче бути C ++, але він зрілий і стійкий.

0

У консольних іграх ми ніколи не використовуємо std :: list, оскільки:

  1. вона виконує виділення пам'яті щоразу, коли ви додаєте новий елемент. розподіл пам'яті відбувається повільно.
  2. він повний покажчиків. покажчики погані. покажчик - це помилка кешу. пропустити кеш - це погано.

навіть std :: вектор втрачає прихильність до консолей, оскільки:

  1. вас часто хвилює лише, наприклад, положення всіх об'єктів. наприклад, ви хочете зіткнути об'єкти один з одним, і в цьому випадку вам не байдуже, який їх колір. тому ви хочете, щоб усі позиції були суміжними в пам'яті, і ви хочете, щоб кольори були десь далеко, щоб уникнути забруднення кеша. але std :: vector вимагає, щоб ви зберігали все для кожного об'єкта у суміжній частині пам’яті (наприклад, позиція, то колір. якщо ви не використовуєте їх. це марно.

5
@bmcnett "[] .. але std :: вектор вимагає, щоб ви зберігали все для кожного об'єкта в суміжній частині пам'яті" - це не питання контейнера, це питання вашої компонування даних, ви можете мати всю позицію у безперервному шматку пам’яті з std :: vector:struct point{float x, y, z, w}; std::vector<point> positions;
Майк Семдер

моїй школі сподобається це :)
jokoon

2
-1 тому, що ця відповідь не додає нічого до списку проти векторної дискусії, яка вже тут, і його твердження про вектор, що забруднює кеш, є неправильним, як каже Майк.

ви неправильно зрозуміли мою претензію. я ніколи не говорив, що серед усіх контейнерів std :: vector особливо винен у забрудненні кешу. всі контейнери, а насправді навіть масиви, приблизно однаково винні в цьому. std :: вектор випадає з користі, оскільки самі об'єкти C ++ випадають з користі. C ++ вимагає, щоб дані кожного об'єкта були суміжними в пам'яті. ви можете обійти це, уникаючи C ++ об'єктів, наприклад, використовуючи std :: vector <position> як зазначено вище.
bmcnett

3
За винятком pointє об'єкт C ++ (як це є std::vector, як і щось таке просто float). Я знаю відмінність, яку ви намагаєтеся намалювати, але ви робите погану роботу, пояснюючи це.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.