Коли використовувати LinkedList над ArrayList на Java?


3122

Я завжди використовував:

List<String> names = new ArrayList<>();

Я використовую інтерфейс як назву типу для портативності , так що коли я задаю такі питання, я можу переробити свій код.

Коли слід LinkedListвикористовувати більше ArrayListі навпаки?



1
Просто перегляньте цитату автора LinkedList stackoverflow.com/a/42529652/2032701, і ви отримаєте практичне розуміння проблеми.
Руслан

Відповіді:


3371

Резюме ArrayList з ArrayDequeкращими є кращими в багатьох інших випадках використання, ніж LinkedList. Якщо ви не впевнені - почніть з ArrayList.


LinkedListі ArrayListце дві різні реалізації інтерфейсу List. LinkedListреалізує його з подвійно пов'язаним списком. ArrayListреалізує його за допомогою масиву, що динамічно переосмислює.

Як і у стандартних операціях зі списком та масивом, різні методи матимуть різні алгоритмічні умови виконання.

Для LinkedList<E>

  • get(int index)є O (n)n / 4 кроками в середньому), але O (1), коли index = 0або index = list.size() - 1(у цьому випадку ви також можете використовувати getFirst()і getLast()). Одна з головних переваг LinkedList<E>
  • add(int index, E element)є O (n)n / 4 кроками в середньому), але O (1), коли index = 0або index = list.size() - 1(у цьому випадку ви також можете використовувати addFirst()та addLast()/ add()). Одна з головних переваг LinkedList<E>
  • remove(int index)є O (n)n / 4 кроками в середньому), але O (1), коли index = 0або index = list.size() - 1(у цьому випадку ви також можете використовувати removeFirst()і removeLast()). Одна з головних переваг LinkedList<E>
  • Iterator.remove()є O (1) . Одна з головних переваг LinkedList<E>
  • ListIterator.add(E element)є O (1) . Одна з головних переваг LinkedList<E>

Примітка: Для багатьох операцій в середньому потрібні n / 4 кроки, постійне число кроків у кращому випадку (наприклад, індекс = 0) та n / 2 кроки в гіршому випадку (середина списку)

Для ArrayList<E>

  • get(int index)є O (1) . Основна вигода ArrayList<E>
  • add(E element)є O (1) амортизованим, але O (n) найгіршим випадком, оскільки масив повинен бути змінений і скопійований
  • add(int index, E element)є O (n) (із середнім n / 2 кроками)
  • remove(int index)є O (n) (із середнім n / 2 кроками)
  • Iterator.remove()є O (n) (із середнім n / 2 кроками)
  • ListIterator.add(E element)є O (n) (із середнім n / 2 кроками)

Примітка: для багатьох операцій в середньому потрібно n / 2 кроки, постійне число кроків у кращому випадку (кінець списку), n кроків у гіршому (початок списку)

LinkedList<E>дозволяє постійно вставляти чи видаляти за допомогою ітераторів , але лише послідовний доступ до елементів. Іншими словами, ви можете ходити по списку вперед або назад, але пошук позиції в списку займає час, пропорційний розміру списку. Javadoc каже, що "операції, що індексують список, будуть перетинати список від початку чи до кінця, залежно від того, що ближче" , тож ці методи O (n) ( n / 4 кроки), хоча O (1) для index = 0.

ArrayList<E>з іншого боку, дозволяють отримати швидкий випадковий доступ для читання, тому ви можете захоплювати будь-який елемент за постійний час. Але додавання або видалення з будь-якого місця, але для кінця вимагає зміщення всіх останніх елементів, щоб зробити відкриття або заповнити проміжок. Крім того, якщо ви додасте більше елементів, ніж ємність основного масиву, виділяється новий масив (у 1,5 рази більше), а старий масив копіюється в новий, тож додається доArrayList є O (n) в гіршому випадок, але постійний в середньому.

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

Основні переваги використання LinkedListвиникають при повторному використанні існуючих ітераторів для вставки та видалення елементів. Потім ці операції можна виконати в O (1) , змінивши лише список локально. У списку масивів решту масиву потрібно перемістити (тобто скопіювати). З іншого боку, шукати LinkedListзасіб, що слідує за посиланнями в O (n) ( n / 2 кроки) в гіршому випадку, тоді як у ArrayListбажаному положенні можна обчислити математично і отримати доступ до нього в O (1) .

Ще одна перевага використання LinkedListвиникає при додаванні або видаленні з голови списку, оскільки ці операції O (1) , в той час як вони O (п) для ArrayList. Зауважте, що це ArrayDequeможе бути хорошою альтернативою LinkedListдля додавання та видалення з голови, але це не є List.

Крім того, якщо у вас є великі списки, майте на увазі, що використання пам'яті також відрізняється. Кожен елемент a LinkedListмає більше накладних витрат, оскільки вказівники на наступний та попередні елементи також зберігаються. ArrayListsне мають цього накладних витрат. Однак,ArrayLists займіть стільки пам’яті, скільки відведено на ємність, незалежно від того, чи були фактично додані елементи.

Початкова потужність за замовчуванням ArrayListє досить невеликою (10 з Java 1.4 - 1.8). Але оскільки основна реалізація є масивом, масив повинен бути змінений, якщо ви додасте багато елементів. Щоб уникнути високої вартості зміни розміру, коли ви знаєте, що збираєтеся додати багато елементів, сконструюйте конструкцію ArrayListз більшою початковою ємністю.


182
Забув згадати витрати на введення. Після того, як у вас є правильне положення, у LinkedList, вставка коштує O (1), а в ArrayList - до O (n) - всі елементи, які минули точку вставки, повинні бути переміщені.
Девід Родрігес - дрибес

26
Щодо використання вектора: насправді немає необхідності повертатися до Vector. Це можна зробити за допомогою вашої вподобаної реалізації списку та виклику до синхронізованого списку, щоб надати йому синхронізовану обгортку. Дивіться: java.sun.com/docs/books/tutorial/collections/implementations/…
Райан Кокс

69
Ні, для LinkedList, отримати все одно O (n), навіть якщо ви знаєте позицію, тому що для того, щоб дійти до цієї позиції, основна реалізація повинна пройти "наступні" покажчики пов'язаного списку, щоб досягти значення цієї позиції. Не існує такого поняття, як випадковий доступ. Для позиції 2 ходити вказівниками може бути дешево, але для позиції 1 мільйон, не так вже й дешево. Справа в тому, що вона пропорційна позиції, це означає, що це O (n).
Джонатан Тран

53
@Kevin Це може мати значення, що пам'ять "близько один до одного". Обладнання буде кешувати суміжні блоки пам'яті (динамічна оперативна пам'ять) в більш швидку статичну ОЗУ в кеш-пам'яті L1 або L2. Теоретично і більшу частину часу практично до пам'яті можна трактувати як випадковий доступ. Але насправді читання через пам'ять послідовно буде трохи швидшим, ніж у випадковому порядку. Для критичного циклу, це може мати значення. Вони називають це "просторова місцевість" або місце відліку .
Джонатан Тран

92
Там немає такого поняття , як O(n/2)або O(n/4). Велика нотація O говорить про те, як операція масштабується з більшим n . і операція, що потребує n/2кроків, масштабується точно так само, як операція n, яка потребує кроків, і це є причиною того, що постійні суми або фактори видаляються O(n/2)і O(n/4)обидва просто O(n). LinkedListі в ArrayListбудь-якому випадку мають різні постійні фактори, тому не було б сенсу порівнювати O(n/2)один з O(n/4)другим, обидва позначають операції лінійного масштабування.
Холгер

630

Поки, здається, ніхто не звертався до пам’яті пам’яті кожного з цих списків, окрім загальної думки про те, що а LinkedList«набагато більше», ніжArrayList так, я зробив певну кількість хрускіт, щоб продемонструвати, наскільки точно обидва списки займають N нульових посилань.

Оскільки посилання є або 32, або 64 бітами (навіть якщо вони є нульовими) у їх відносних системах, я включив 4 набори даних для 32 та 64 біт LinkedListsіArrayLists .

Примітка . Розміри, показані для ArrayListрядків, призначені для оброблених списків. На практиці ємність резервного масиву вArrayList як правило, більше, ніж його кількість поточних елементів.

Примітка 2: (спасибі BeeOnRope) Оскільки CompressionOops тепер за замовчуванням починаючи з середини JDK6 і вище, значення нижче для 64-розрядних машин в основному будуть відповідати їх 32-бітовим аналогам, якщо, звичайно, ви спеціально не відключаєте його.


Графік LinkedList та ArrayList Кількість елементів х байтів


Результат чітко показує, що LinkedListце набагато більше ArrayList, особливо з дуже високою кількістю елементів. Якщо пам'ять є фактором, уникайте цього LinkedLists.

Формули, які я використав, слідують, повідомте мені, чи зробив я щось не так, і я виправлю це. 'b' або 4, або 8 для 32 або 64 бітових систем, а 'n' - кількість елементів. Зауважте, що причина модів полягає в тому, що всі об’єкти в Java будуть займати кратне 8 байтного простору, незалежно від того, використовується він чи ні.

ArrayList:

ArrayList object header + size integer + modCount integer + array reference + (array oject header + b * n) + MOD(array oject, 8) + MOD(ArrayList object, 8) == 8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8) + MOD(8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8), 8)

LinkedList:

LinkedList object header + size integer + modCount integer + reference to header + reference to footer + (node object overhead + reference to previous element + reference to next element + reference to element) * n) + MOD(node object, 8) * n + MOD(LinkedList object, 8) == 8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n + MOD(8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n, 8)


2
Досить цікаво побачити, що LinkedList вимагає стільки ж пам’яті, скільки ArrayList для зберігання одного елемента. Як неінтуїтивно! Що станеться, якщо ви запустите свій приклад за допомогою -XX: + UseCompressionOops?
jontejj

215
Проблема вашої математики полягає в тому, що ваш графік сильно перебільшує вплив. Ви моделюєте об'єкти, кожен з яких містить лише int4, або 8 байт даних. У пов'язаному списку фактично є 4 "слова" накладних. Таким чином, ваш графік створює враження, що пов'язані списки "п’ять разів" зберігають списки масивів. Це неправильно. Накладні витрати становлять 16 або 32 байти на об’єкт, як коригування добавки, а не коефіцієнта масштабування.
Хіт Ханнікутт

6
Жоден з об’єктів ArrayList / LinkedList / Node не містить лише int, тому я не отримую те, що ви там говорите. Я переформулював "об’єкт накладних витрат" на "заголовок об'єкта", щоб уточнити - у кожному об'єкті, незалежно від системи, є 8-байтний заголовок, і так, він включає в себе всі об'єкти "Вузола" в LinkedList, які всі підраховуються правильно, наскільки я можу скажи. Між іншим, дивлячись на це ще раз, я виявив кілька інших проблем з моєю математикою в LinkedList, що насправді робить його поділ і ArrayList гіршими . Я радий продовжувати його оновлювати, тому, будь ласка, не соромтеся уточнювати та розробити детальніше.
Нумерон

6
Слід зазначити, що CompressedOopsзараз за замовчуванням у всіх останніх JDK (7, 8 та оновлення 6 протягом декількох років), тому 64-розрядні не змінитимуть ArrayListчи LinkedListрозміри, якщо ви явно не вимкнули стиснуті функції для чомусь.
BeeOnRope

1
@jontejj збільшення пропускної здатності за замовчуванням становить 50%, тому коли ви заповнюєте ArrayListбез вказівки початкової ємності, вона все одно буде використовувати значно менше пам'яті, ніж a LinkedList.
Холгер

243

ArrayListце те, що ти хочеш. LinkedListмайже завжди є (продуктивна) помилка.

Чому LinkedListсмокче:

  • Він використовує безліч невеликих об'єктів пам'яті, а тому впливає на продуктивність у процесі.
  • Багато дрібних предметів погано впливають на кеш-локальність.
  • Будь-яка індексована операція вимагає обходу, тобто має O (n) продуктивність. Це не очевидно у вихідному коді, що призводить до того, що алгоритми O (n) повільніше, ніж якби вони ArrayListвикористовувались.
  • Отримати хороші показники складно.
  • Навіть коли продуктивність з великим O така ж, як і раніше ArrayList, це, мабуть, буде значно повільніше.
  • Це бачити LinkedListв джерелі, тому що це, мабуть, неправильний вибір.

236
Вибачте. позначив вас. LinkedList не смокче. Бувають ситуації, коли LinkedList є правильним класом для використання. Я погоджуюся, що не так вже й багато ситуацій, коли це краще, ніж аррейліст, але вони існують. Навчайте людей, які роблять дурні речі!
Девід Тернер

40
Вибачте за те, що ви отримали багато голосів за це. Справді дуже мало причин використовувати LinkedList Java. Крім поганої продуктивності, він також використовує набагато більше пам’яті, ніж інші конкретні класи списку (кожен вузол має два додаткові вказівники, і кожен вузол є окремим об’єктом обгортки з додатковими накладними байтами, які йдуть разом з ними).
Кевін Брок

42
Це одна з найкорисніших відповідей тут. Прикро, що багато програмістів не розуміють (а) різницю між абстрактними типами даних та конкретними реалізаціями та (б) важливість постійних факторів та витрат на пам'ять у реальному світі для визначення продуктивності.
Porculus

50
-1: Це досить миготливий погляд. Так, це правда, що ArrayList - це дуже універсальний інструмент. Однак це має свої обмеження. Є випадки, коли це спричинить вам проблеми, і вам доведеться використовувати LinkedList. Звичайно, це дуже спеціалізоване рішення, і, як і будь-який спеціалізований інструмент, у більшості випадків воно перевершує універсальний. Але це не означає, що воно "смокче" або щось подібне, ви просто повинні знати, коли ним користуватися.
Малькольм

27
@DavidTurner: Вони існують, але я думаю, що Тома в тому, що якщо вам доведеться запитати, ви, мабуть, хочете ArrayList.
користувач541686

139

Як хтось, хто вже близько десяти років займається інженерією операційної ефективності на дуже великих масштабах веб-сервісів SOA, я вважаю за краще поведінку LinkedList над ArrayList. Хоча стабільна пропускна здатність LinkedList гірша і, отже, може призвести до придбання більше обладнання - поведінка ArrayList під тиском може призвести до того, що додатки в кластері розширять їх масиви майже в синхронності, а для великих розмірів масиву може призвести до недостатньої реакції у додатку та перебої, хоча під тиском, що катастрофічно.

Аналогічно, ви можете отримати кращу пропускну здатність у додатку від збору сміття за промовчанням за замовчуванням, але як тільки ви отримаєте програми java з набором 10 ГБ, ви зможете завершити роботу програми на 25 секунд протягом повних ГК, що спричиняє очікування та збої в додатках SOA і дме ваші угоди про угоду, якщо це трапляється занадто часто. Навіть незважаючи на те, що колектор CMS забирає більше ресурсів і не досягає такої ж пропускної здатності, він є набагато кращим вибором, оскільки він має більш передбачувані та менші затримки.

ArrayList - лише кращий вибір для продуктивності, якщо все, що ви маєте на увазі під продуктивністю, є пропускною здатністю і ви можете ігнорувати затримки. З мого досвіду роботи я не можу ігнорувати найгірші випадки затримки.


8
Чи не було б іншим рішенням керувати розміром списку програмно, використовуючи метод sureCapacity () ArrayList? Моє запитання: чому стільки речей зберігається в купі крихких структур даних, коли їх краще зберігати в механізмі кешування або db? Днями у мене було інтерв'ю, де вони клялися вгору і вниз про зло ArrayList, але я приїжджаю сюди і знаходжу, що аналіз складності всебічно кращий! ВЕЛИКИЙ ОКРЕМ ДЛЯ ДИСКУСІЇ, ДУМКИ. ДЯКУЮ!
ingyhere

22
щойно ви отримуєте програми java з набором 10 Гб, ви можете завершити блокування програми на 25 секунд під час повноцінного використання програмного забезпечення, що спричиняє тайм-аути. Насправді з LinkedList ви вбиваєте збирач сміття під час повних ГК, він повинен ітератувати надмірно великий LinkedList з пропуском кешу кожен вузол.
bestsss

5
Це ... жахливе рішення. ви в основному покладаєтесь на прибирання GC для вас, що є надзвичайно дорогим, коли ви можете просто зателефонувати за допомогоюCapacity () на арабський список ...
Philip Devine

5
@Andreas: A LinkedList завжди виділяє в п'ять разів більше пам'яті, ніж звичайний масив посилань, тому ArrayListтимчасово вимагає 2,5 рази все-таки витрачається набагато менше пам’яті, навіть поки пам’ять не відновлена. Оскільки великий розподіл масиву обходить простір Едена, вони не впливають на поведінку GC, якщо справді не вистачає пам’яті, в цьому випадку LinkedListвибухнув набагато раніше…
Holger

5
@Andreas Інше питання - як розподіляється пам'ять. LinkedListпотрібно лише невеликий шматочок вільної пам'яті, щоб виділити наступний елемент. ArrayListзнадобиться великий і нескінченний вільний блок простору для виділення зміненого масиву. Якщо купа роздроблена, GC може в кінцевому підсумку переупорядкувати всю купу просто для звільнення відповідного єдиного блоку пам'яті.
Piotr Kusmierczyk

128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

Алгоритми: Позначення великого розміру

ArrayLists корисні для запису один раз для читання чи багатьох додатків, але погані для додавання / видалення з передньої чи середини.


42
Ви не можете безпосередньо порівнювати величини великого O, не думаючи про постійні фактори. Для невеликих списків (а більшість списків невеликі), O (N) ArrayList швидше, ніж O (1) LinkedList.
Porculus

4
Мене не хвилює ефективність невеликих списків, а також мій комп'ютер, якщо якимось чином не використовується в циклі.
Maarten Bodewes

45
LinkedList насправді не може вставити посередині O(1). Він повинен пройти через половину списку, щоб знайти точку вставки.
Томас Ейл

8
LinkedList: вставити в середину O (1) - НЕПРАВНО! Я з'ясував, що навіть вставлення в 1/10 позицію розміру LinkedList повільніше, ніж вставлення елемента в 1/10 позицію ArrayList. І ще гірше: кінець колекції. вставлення в останні позиції (не самі останні) ArrayList швидше, ніж в останні позиції (не самі останні)
LinkedList

14
@kachanov Вставлення в a LinkedList - це O(1) якщо у вас є ітератор до позиції вставки , тобто ListIterator.addнібито O(1)для a LinkedList.
Мав QUIT - Anonymous-Mousse

107

Так, я знаю, це давнє питання, але я кину два мої центи:

LinkedList майже завжди є неправильним вибором, залежно від продуктивності. Існує кілька дуже специфічних алгоритмів, для яких закликається LinkedList, але вони дуже, дуже рідкісні, і алгоритм, як правило, конкретно залежатиме від здатності LinkedList вставляти та видаляти елементи в середині списку відносно швидко, як тільки ви перейдете туди. за допомогою ListIterator.

Є один поширений випадок використання, в якому LinkedList перевершує ArrayList: черга. Однак якщо ваша мета - ефективність, замість LinkedList вам слід також розглянути можливість використання ArrayBlockingQueue (якщо ви можете достроково визначити верхню межу розміру своєї черги і можете дозволити собі виділити всю пам'ять на передній частині) або цю реалізацію CircularArrayList . (Так, це з 2001 року, тому вам потрібно буде це узагальнити, але я отримав порівнянні коефіцієнти продуктивності з тими, що цитуються в статті недавно в недавньому JVM)


39
З Java 6 ви можете використовувати ArrayDeque. docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
Томас Ейл

1
ArrayDequeвідбувається повільніше, ніж LinkedListякщо всі операції не мають одного кінця. Це нормально, коли використовується як стек, але це не робить хорошої черги.
Список Джеремі

2
Неправда - принаймні для реалізації Oracle у jdk1.7.0_60 та у наступному тесті. Я створив тест, де циклічно проробляю 10 мільйонів разів, і у мене є Дека з 10 мільйонів випадкових цілих чисел. Всередині петлі я запитую один елемент і пропоную постійний елемент. На моєму комп’ютері LinkedList в 10 разів повільніше, ніж ArrayDeque і використовує менше пам’яті). Причина полягає в тому, що на відміну від ArrayList, ArrayDeque зберігає вказівник на голову масиву, щоб йому не потрібно було переміщувати всі елементи, коли голова видалена.
Генно Вермеулен

6
ArrayDequeймовірно, буде швидше, ніж Stackколи використовується як стек, і швидше, ніж LinkedListтоді, коли використовується як черга.
akhil_mittal

3
Зауважте, що коментар akhil_mittal - це цитата з ArrayDequeдокументації.
Стюарт відзначає

65

Це питання ефективності. LinkedListшвидкий для додавання та видалення елементів, але повільний для доступу до певного елемента. ArrayListшвидкий доступ до певного елемента, але його можна повільно додавати в будь-який кінець, і особливо повільно видаляти в середині.

Array vs ArrayList vs LinkedList vs Vector іде більш глибоко, як і пов'язаний список .


54

Правильно чи неправильно. Виконайте тест на місцях та вирішіть самі!

Редагування / видалення швидше, LinkedListніж ArrayList.

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

Нижче наведено одиничний результат випробування для кожної операції. Томічне значення наведено в наносекундах.


Operation                       ArrayList                      LinkedList  

AddAll   (Insert)               101,16719                      2623,29291 

Add      (Insert-Sequentially)  152,46840                      966,62216

Add      (insert-randomly)      36527                          29193

remove   (Delete)               20,56,9095                     20,45,4904

contains (Search)               186,15,704                     189,64,981

Ось код:

import org.junit.Assert;
import org.junit.Test;

import java.util.*;

public class ArrayListVsLinkedList {
    private static final int MAX = 500000;
    String[] strings = maxArray();

    ////////////// ADD ALL ////////////////////////////////////////
    @Test
    public void arrayListAddAll() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        arrayList.addAll(stringList);
        watch.totalTime("Array List addAll() = ");//101,16719 Nanoseconds
    }

    @Test
    public void linkedListAddAll() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);

        watch.start();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);
        watch.totalTime("Linked List addAll() = ");  //2623,29291 Nanoseconds
    }

    //Note: ArrayList is 26 time faster here than LinkedList for addAll()

    ///////////////// INSERT /////////////////////////////////////////////
    @Test
    public void arrayListAdd() {
        Watch watch = new Watch();
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        for (String string : strings)
            arrayList.add(string);
        watch.totalTime("Array List add() = ");//152,46840 Nanoseconds
    }

    @Test
    public void linkedListAdd() {
        Watch watch = new Watch();

        List<String> linkedList = new LinkedList<String>();
        watch.start();
        for (String string : strings)
            linkedList.add(string);
        watch.totalTime("Linked List add() = ");  //966,62216 Nanoseconds
    }

    //Note: ArrayList is 9 times faster than LinkedList for add sequentially

    /////////////////// INSERT IN BETWEEN ///////////////////////////////////////

    @Test
    public void arrayListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX + MAX / 10);
        arrayList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        arrayList.add(insertString0);
        arrayList.add(insertString1);
        arrayList.add(insertString2);
        arrayList.add(insertString3);

        watch.totalTime("Array List add() = ");//36527
    }

    @Test
    public void linkedListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        linkedList.add(insertString0);
        linkedList.add(insertString1);
        linkedList.add(insertString2);
        linkedList.add(insertString3);

        watch.totalTime("Linked List add = ");//29193
    }


    //Note: LinkedList is 3000 nanosecond faster than ArrayList for insert randomly.

    ////////////////// DELETE //////////////////////////////////////////////////////
    @Test
    public void arrayListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.remove(searchString0);
        arrayList.remove(searchString1);
        watch.totalTime("Array List remove() = ");//20,56,9095 Nanoseconds
    }

    @Test
    public void linkedListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.remove(searchString0);
        linkedList.remove(searchString1);
        watch.totalTime("Linked List remove = ");//20,45,4904 Nanoseconds
    }

    //Note: LinkedList is 10 millisecond faster than ArrayList while removing item.

    ///////////////////// SEARCH ///////////////////////////////////////////
    @Test
    public void arrayListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.contains(searchString0);
        arrayList.contains(searchString1);
        watch.totalTime("Array List addAll() time = ");//186,15,704
    }

    @Test
    public void linkedListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.contains(searchString0);
        linkedList.contains(searchString1);
        watch.totalTime("Linked List addAll() time = ");//189,64,981
    }

    //Note: Linked List is 500 Milliseconds faster than ArrayList

    class Watch {
        private long startTime;
        private long endTime;

        public void start() {
            startTime = System.nanoTime();
        }

        private void stop() {
            endTime = System.nanoTime();
        }

        public void totalTime(String s) {
            stop();
            System.out.println(s + (endTime - startTime));
        }
    }


    private String[] maxArray() {
        String[] strings = new String[MAX];
        Boolean result = Boolean.TRUE;
        for (int i = 0; i < MAX; i++) {
            strings[i] = getString(result, i);
            result = !result;
        }
        return strings;
    }

    private String getString(Boolean result, int i) {
        return String.valueOf(result) + i + String.valueOf(!result);
    }
}

1
ArrayList не потрібно подвоювати, щоб бути точним. Спочатку перевірте джерела.
Дунайський матрос

Слід зазначити, що ваш приклад є помилковим ... Ви видаляєте з рядка між: 18 + [2, 12] байтами ("true0false", "true500000false"), в середньому 25 байт, які є розмірами елементів посередині. Відомо, що в міру збільшення розміру байта елементів зв'язаний список працює краще, оскільки розмір списку збільшується, суміжний масив (список) буде краще. Найголовніше, що ви робите .equals () на рядках - що не є дешевою операцією. Якщо ви замість цього використали цілі числа, я думаю, що це було б різницею.
Сентрил

2
"... гірше в застосуванні великих обсягів ": Це непорозуміння. LinkedListмає набагато більше оперативної пам'яті, тому що для кожного елемента існує вузольний об’єкт із п’ятьма полями. У багатьох системах, що робить 20 байт накладних витрат. Середній накладний обсяг пам’яті на один елемент для ArrayList- це півтора слова, що складає 6 байт, а в гіршому 8 байт.
Лій

1
Тут я зробив кращу версію вашого орієнтиру , з результатами - продуктивність додатка в кінці для арселіста штучно низька для вашої, тому що addAll надає масив зберігання ТОЧНО початкового розміру, тому перша вставка завжди запускає арракопія. Крім того, це включає цикли розминки, щоб забезпечити компіляцію JIT до збору даних.
BobMcGee

4
@BillK з Java 8, ви можете використовувати removeIf(element -> condition)там, де вона підходить, що може бути значно швидше для ArrayList, порівняно з циклічним і видаленням за допомогою ітератора, оскільки не потрібно зміщувати весь залишок для кожного окремого елемента. Чи буде це краще чи гірше, ніж LinkedListзалежить від конкретного сценарію, як це LinkedListтеоретично O (1), але для видалення лише одного вузла потрібно кілька доступу до пам'яті, що може легко перевищити кількість, необхідне для ArrayListвидалення значної кількості елементів .
Холгер

50

ArrayListпо суті є масивом. LinkedListреалізується у вигляді подвійного зв'язаного списку.

Це getдосить зрозуміло. O (1) для ArrayList, тому що ArrayListдозволяють випадковий доступ за допомогою індексу. O (n) для LinkedList, тому що для цього потрібно знайти індекс. Примітка. Існують різні версії addта remove.

LinkedListшвидше додавати та видаляти, але повільніше отримувати. Якщо коротко, LinkedListслід віддати перевагу, якщо:

  1. немає великої кількості випадкового доступу до елемента
  2. існує велика кількість операцій додавання / видалення

=== ArrayList ===

  • додати (E e)
    • додати в кінці ArrayList
    • вимагають зміни розміру пам'яті.
    • O (n) найгірший, O (1) амортизований
  • додати (int index, елемент E)
    • додати до певної позиції індексу
    • вимагають перемикання та можливої ​​зміни розміру пам'яті
    • O (n)
  • видалити (int індекс)
    • видаліть вказаний елемент
    • вимагають перемикання та можливої ​​зміни розміру пам'яті
    • O (n)
  • видалити (Об'єкт o)
    • видаліть із цього списку перше виникнення зазначеного елемента
    • спочатку потрібно шукати елемент, а потім зміщувати & можливі зміни розміру пам'яті
    • O (n)

=== LinkedList ===

  • додати (E e)

    • додати до кінця списку
    • O (1)
  • додати (int index, елемент E)

    • вставити у вказане положення
    • потрібно спочатку знайти позицію
    • O (n)
  • видалити ()
    • видалити перший елемент списку
    • O (1)
  • видалити (int індекс)
    • видалити елемент із заданим індексом
    • потрібно спочатку знайти елемент
    • O (n)
  • видалити (Об'єкт o)
    • видалити перше виникнення зазначеного елемента
    • потрібно спочатку знайти елемент
    • O (n)

Ось цифра з programcreek.com ( addі removeє першим типом, тобто додайте елемент в кінці списку і видаліть елемент у вказаному положенні в списку.):

введіть тут опис зображення


3
"LinkedList швидше, ніж додавання / видалення". Неправильно, перевірте відповідь вище stackoverflow.com/a/7507740/638670
Nerrve

49

Джошуа Блох, автор LinkedList:

Хтось насправді використовує LinkedList? Я написав це, і ніколи його не використовую.

Посилання: https://twitter.com/joshbloch/status/583813919019573248

Мені дуже шкода відповіді за те, що вона не така інформативна, як інші відповіді, але я подумала, що це буде найцікавіше і зрозуміло.


34

ArrayListдоступний випадковим чином, але LinkedListрозгортати та видаляти елементи з них дуже дешево. У більшості випадків ArrayListце нормально.

Якщо ви не створили великі списки і не виміряли вузьке місце, вам, ймовірно, ніколи не потрібно буде турбуватися про різницю.


15
LinkedList не дешево додавати елементи. Майже завжди швидше додати мільйон елементів до ArrayList, ніж додати їх до LinkedList. І більшість списків у реальному коді не мають навіть мільйона елементів.
Porculus

10
У будь-який момент ви знаєте вартість додавання елемента до свого LinkedList. ArrayList у вас немає (загалом). Додавання одного елемента до ArrayList, що містить мільйон елементів, може зайняти дуже багато часу - це операція O (n) плюс подвоєння пам’яті, якщо ви не виділили простір. Додавання елемента до LinkedList - це O (1). Моє останнє твердження стоїть.
Дастін

4
Додавання одного елемента до ArrayList - це O (1), незалежно від того, 1 мільйон чи 1 мільярд. Додавання елемента до LinkedList також є O (1). "Додавання" означає додавання до кінця.
качанів

Ви, мабуть, читали реалізацію інакше, ніж я. На мій досвід, копіювання масиву на 1 мільярд елементів займає більше часу, ніж копіювання масиву на 1 мільйон елементів.
Дастін

6
@kachanov ви повинні неправильно зрозуміти Дастін. Якщо ви не оголосили масив з 1 мільярда елементів, вам, зрештою, потрібно буде змінити розмір масиву, і тоді вам доведеться скопіювати всі елементи в новий більший масив, отже, іноді ви отримаєте O (N), однак із пов'язаним списком ви завжди отримати O (1)
Стен Р.

29

TL; DR завдяки сучасній комп'ютерній архітектурі ArrayListбуде значно ефективнішим практично для будь-яких можливих випадків використання, а тому LinkedListслід уникати, крім деяких дуже унікальних та крайніх випадків.


Теоретично LinkedList має O (1) для add(E element)

Також додавання елемента в середині списку повинно бути дуже ефективним.

Практика дуже відрізняється, оскільки LinkedList - це структура даних , керованих кешем . Від POV продуктивності - дуже мало випадків, коли LinkedListможна було б бути більш ефективними, ніж кеш-пам'ять ArrayList .

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

введіть тут опис зображення

Робота над апаратним забезпеченням пізнішого покоління (більший, ефективніший кеш) - результати ще більш переконливі:

введіть тут опис зображення

LinkedList потребує набагато більше часу, щоб виконати ту саму роботу. вихідний вихідний код

Для цього є дві основні причини:

  1. Головне - щоб вузли LinkedListрозкидані випадковим чином по пам’яті. Оперативна пам’ять ("Пам'ять випадкового доступу") насправді не випадкова, і блоки пам'яті потрібно отримати в кеш. Ця операція потребує часу, і коли такі вилучення трапляються часто - сторінки пам'яті в кеші потрібно постійно замінювати -> Кеш пропускає -> Кеш не є ефективним. ArrayListелементи зберігаються у постійній пам'яті - саме для цього оптимізована сучасна архітектура процесора.

  2. Вторинний, LinkedList необхідний для утримування покажчиків назад / вперед, що означає 3-кратне споживання пам'яті на значення, що зберігається порівняно з ArrayList.

DynamicIntArray , btw, є власною програмою реалізації ArrayListInt (примітивний тип), а не об'єктами - отже, всі дані дійсно зберігаються поруч - отже, ще ефективніше.

Ключові елементи, які слід пам’ятати, - це те, що вартість отримання блоку пам’яті є більш значною, ніж вартість доступу до однієї комірки пам’яті. Ось чому зчитування 1 Мб послідовної пам’яті до x400 разів швидше, ніж зчитування такої кількості даних з різних блоків пам’яті:

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

Джерело: Номери затримки, які повинен знати кожен програміст

Щоб зробити пункт ще зрозумілішим, будь ласка, перевірте орієнтир додавання елементів до початку списку. Це випадок використання, коли, теоретично, LinkedListслід дійсно світитись і ArrayListповинен мати слабкі або навіть гірші випадки:

введіть тут опис зображення

Примітка: це орієнтир лінзу C ++ Std, але мій попередній досвід показав, що результати C ++ та Java дуже схожі. Вихідний код

Копіювання послідовної маси пам'яті - це операція, оптимізована сучасними процесорами - зміна теорії і фактично, знову ж таки, ArrayList/ Vectorнабагато ефективніше


Кредити: Усі розміщені тут орієнтири створені Kjell Hedström . Ще більше даних можна знайти в його блозі


Я б не назвав чергу унікальною чи екстремальною! Черга fifo набагато простіше реалізується на LinkedList замість ArrayList. Це насправді кошмар у ArrayList, оскільки вам потрібно відстежувати власний початок, зупинку та робити власне перерозподілення, ви також можете скористатися масивом, але список пов'язаних груп - це фіфо. Я не впевнений у впровадженні Java, але LinkedList може робити O (1) як для черги, так і для операцій з видаленням (Потрібен спеціальний вказівник на елемент хвоста для видалення. .)
Білл К

24

Якщо у вашому коді є add(0)і remove(0), використовуйте a, LinkedListі це гарніше addFirst()і removeFirst()методи. В іншому випадку використовуйте ArrayList.

І звичайно, Guava 's ImmutableList - ваш найкращий друг.


3
Для невеликих списків ArrayList.add (0) все ще буде швидше, ніж LinkedList.addFirst ().
Porculus

1
@Porculus Я постійно чую цей аргумент, що для невеликих списків ArrayList.add (0) буде швидше, це мало на скільки мало? 10 елементів, 10 мільйонів,?
garg10май

1
@ garg10may маленький менше 10.
Джессі Вілсон

@Porculus small означає менше максимальної ємності внутрішнього масиву, що лежить в основі ArrayList.
Janac Meena

21

Я знаю , що це старий пост, але я чесно не можу повірити , що ніхто не говорив , що LinkedListзнаряддя Deque. Просто подивіться на методи в DequeQueue); якщо ви хочете справедливого порівняння, спробуйте побігти LinkedListпроти ArrayDequeта зробіть порівняння для ознак.


18

Ось запис Big-O в обох ArrayListі , LinkedListа такожCopyOnWrite-ArrayList :

ArrayList

get                 O(1)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

LinkedList

get                 O(n)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(1)
iterator.remove     O(1)

CopyOnWrite-ArrayList

get                 O(1)
add                 O(n)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

Виходячи з них, ви повинні вирішити, що вибрати. :)


9
>>>> ArrayList add -> O (1) <- не tru. У деяких випадках ArrayList доведеться зростати, щоб додати ще один елемент
качанов

1
Видалення LinkedList не є O (1), потрібно буде шукати елемент, який потрібно видалити, тому найгірший випадок O (n) та середній O (n / 2)
garg10may

Ні, ні LinkedList.add(), хоча більшість відповідей тут говорять так.
користувач207421

18

Порівняємо LinkedList і ArrayList wrt нижче параметрів:

1. Впровадження

ArrayList - це реалізація змінного масиву інтерфейсу списку, в той час як

LinkedList є двусвязного реалізацією списку інтерфейсу списку.


2. Продуктивність

  • get (int index) або пошукова операція

    ArrayList get (Int index) працює у постійному часі, тобто O (1)

    Час виконання роботи LinkedList get (int index) - O (n).

    Причина того, що ArrayList швидше, ніж LinkedList, полягає в тому, що ArrayList використовує систему, засновану на індексах, для своїх елементів, оскільки внутрішньо використовує структуру даних масиву, з іншого боку,

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

  • операція вставити () або додати (об’єкт)

    Вставки в LinkedList, як правило, швидкі в порівнянні з ArrayList. У LinkedList додавання або вставка - це операція O (1).

    Якщо в ArrayList , якщо масив є повним, тобто найгіршим випадком, є додаткові витрати на зміну розміру масиву та копіювання елементів у новий масив, що робить час виконання операції додавання в ArrayList O (n), інакше це O (1) .

  • видалити (int) операцію

    Операція видалення в LinkedList, як правило, така ж, як ArrayList, тобто O (n).

    У LinkedList є два перевантажені способи видалення. Один є видалити () без жодного параметра, який видаляє заголовка списку і працює в постійний час O (1). Іншим методом видалення в LinkedList є видалити (int) або видалити (Object), який видаляє Об'єкт або int, переданий як параметр. Цей метод обходить LinkedList, поки він не знайде Об'єкт і від'єднає його від початкового списку. Отже, цей спосіб виконання дорівнює O (n).

    Хоча в методі видалення (int) ArrayList передбачається копіювання елементів зі старого масиву в новий оновлений масив, отже, час його виконання дорівнює O (n).


3. Зворотний ітератор

LinkedList можна повторити в зворотному напрямку, використовуючи descendingIterator ()

у ArrayList немає descendingIterator () , тому нам потрібно написати власний код, щоб повторити його над ArrayList у зворотному напрямку.


4. Початкова ємність

Якщо конструктор не перевантажений, то ArrayList створює порожній список початкової ємності 10, а

LinkedList створює лише порожній список без початкової ємності.


5. Пам'ять накладні

Пам'ять накладні в LinkedList більше порівняно з ArrayList, оскільки вузол в LinkedList потребує підтримки адрес наступного та попереднього вузла. Поки

У ArrayList кожен індекс містить лише фактичний об'єкт (дані).


Джерело


18

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

|---------------------|---------------------|--------------------|------------|
|      Operation      |     ArrayList       |     LinkedList     |   Winner   |
|---------------------|---------------------|--------------------|------------|
|     get(index)      |       O(1)          |         O(n)       | ArrayList  |
|                     |                     |  n/4 steps in avg  |            |
|---------------------|---------------------|--------------------|------------|
|      add(E)         |       O(1)          |         O(1)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     | O(n) in worst case  |                    |            |
|---------------------|---------------------|--------------------|------------|
|    add(index, E)    |       O(n)          |         O(n)       | LinkedList |
|                     |     n/2 steps       |      n/4 steps     |            |
|                     |---------------------|--------------------|            |
|                     |                     |  O(1) if index = 0 |            |
|---------------------|---------------------|--------------------|------------|
|  remove(index, E)   |       O(n)          |         O(n)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     |     n/2 steps       |      n/4 steps     |            |
|---------------------|---------------------|--------------------|------------|
|  Iterator.remove()  |       O(n)          |         O(1)       | LinkedList |
|  ListIterator.add() |                     |                    |            |
|---------------------|---------------------|--------------------|------------|


|--------------------------------------|-----------------------------------|
|              ArrayList               |            LinkedList             |
|--------------------------------------|-----------------------------------|
|     Allows fast read access          |   Retrieving element takes O(n)   |
|--------------------------------------|-----------------------------------|
|   Adding an element require shifting | o(1) [but traversing takes time]  |
|       all the later elements         |                                   |
|--------------------------------------|-----------------------------------|
|   To add more elements than capacity |
|    new array need to be allocated    |
|--------------------------------------|

ArrayDeque трохи більше врівноважує речі у напрямку масивів, оскільки вставка / видалення спереду / ззаду - це все О (1), єдине, у чому пов'язаний Список пов'язаний - це додавання / видалення під час проходження (операції Ітератора).
Білл К

14

На додаток до інших аргументів хороших вище, ви повинні помітити ArrayListзнаряддя RandomAccessінтерфейс, в той час як LinkedListзнаряддя Queue.

Отже, якимось чином вони вирішують дещо різні проблеми з різницею ефективності та поведінки (див. Їх перелік методів).


10

Це залежить від того, які операції ви будете робити більше у списку.

ArrayListшвидше отримати доступ до індексованого значення. Набагато гірше при вставці або видаленні об’єктів.

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


2
Щоб дізнатися більше не читайте, просто напишіть код. і ви дізнаєтесь, що реалізація ArrayList швидше, ніж LinkedList при вставці та видаленні.
качанів

8

Список масивів - це по суті масив із методами додавання елементів тощо (і замість цього слід використовувати загальний список). Це сукупність елементів, до яких можна отримати доступ через індексатор (наприклад [0]). Він передбачає перехід від одного пункту до іншого.

Зв'язаний список визначає перехід від одного елемента до іншого (Пункт a -> Пункт b). Такий же ефект можна отримати і зі списком масивів, але пов'язаний список абсолютно говорить про те, який елемент повинен слідувати за попереднім.



7

Важливою особливістю пов'язаного списку (який я не читав в іншій відповіді) є об'єднання двох списків. З масивом це O (n) (+ накладні витрати деяких перерозподілів) із пов'язаним списком, це лише O (1) або O (2) ;-)

Важливо : для Java LinkedListце не відповідає дійсності! Див. Чи існує швидкий метод concat для пов'язаного списку на Java?


2
Як у тому, що? Це може бути правдою для структур даних з пов’язаним списком, але не об'єктом Java LinkList. Ви не можете просто вказати a nextз одного списку на перший вузол у другому списку. Єдиний спосіб полягає в тому, щоб addAll()додавати елементи послідовно, хоча це краще, ніж прокручувати та викликати add()кожен елемент. Щоб зробити це швидко в O (1), вам потрібен клас композиції (наприклад, org.apache.commons.collections.collection.CompositeCollection), але тоді це буде працювати для будь-якого списку / колекції.
Кевін Брок

так, правда. Відповідь я відредагував відповідно. але побачити це відповідь на «як» зробити це з LinkedList: stackoverflow.com/questions/2494031 / ...
Karussell

7

У ArrayList і LinkedList є свої плюси і мінуси.

ArrayList використовує суміжну адресу пам'яті порівняно з LinkedList, який використовує покажчики на наступний вузол. Отже, коли ви хочете шукати елемент у ArrayList, це швидше, ніж робити n ітерацій з LinkedList.

З іншого боку, вставлення та видалення в LinkedList набагато простіше, тому що вам просто потрібно змінити покажчики, тоді як ArrayList передбачає використання операції shift для будь-якої вставки або видалення.

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


6

Я прочитав відповіді, але є один сценарій, коли я завжди використовую LinkedList над ArrayList, яким хочу поділитися, щоб почути думки:

Кожен раз, коли у мене був метод, який повертає список даних, отриманих з БД, я завжди використовую LinkedList.

Моє обґрунтування полягало в тому, що оскільки неможливо точно знати, скільки результатів я отримую, пам’яті не буде витрачено (як в ArrayList з різницею між ємністю та фактичною кількістю елементів), і не було б витраченого часу на спробу дублювати ємність.

Що стосується ArrayList, я погоджуюся, що принаймні ви завжди повинні використовувати конструктор з початковою ємністю, щоб максимально мінімізувати дублювання масивів.


5

ArrayListі LinkedListобидва знаряддя List interface і їх методи і результати практично ідентичні. Однак є небагато відмінностей між ними, які роблять одну кращу над іншою залежно від вимоги.

ArrayList Vs LinkedList

1) Search: ArrayListпошукова операція є досить швидкою порівняно з LinkedListпошуковою. get(int index)в ArrayListдає результативність в O(1)той час, як LinkedListпродуктивність єO(n) .

Reason: ArrayListпідтримує систему на основі індексу для своїх елементів, оскільки вона використовує структуру даних масиву неявно, що прискорює пошук елемента в списку. З іншого бокуLinkedList реалізується подвійно пов'язаний список, який вимагає проходження всіх елементів для пошуку елемента.

2) Deletion: LinkedListоперація видалення дає O(1)продуктивність, в той час як ArrayListдає змінну продуктивність: O(n)в гіршому випадку (під час видалення першого елемента) і O(1)в кращому випадку (під час видалення останнього елемента).

Висновок: Видалення елементів LinkedList швидше порівняно з ArrayList.

Причина: кожен елемент LinkedList підтримує два покажчики (адреси), які вказують на обидва сусідні елементи у списку. Отже, для видалення потрібна лише зміна місця розташування вказівника у двох сусідніх вузлах (елементах) вузла, який буде видалено. Хоча в ArrayList всі елементи потрібно змістити, щоб заповнити простір, створений видаленим елементом.

3) Inserts Performance: LinkedListметод додавання дає O(1)продуктивність, а ArrayListдає O(n)в гіршому випадку. Причина така ж, як пояснюється для видалення.

4) Memory Overhead: ArrayListпідтримує індекси та дані елементів, LinkedListзберігаючи дані про елементи та два вказівники для сусідніх вузлів

отже, споживання пам'яті у LinkedList порівняно високе.

Є кілька подібностей між цими класами, які є наступними:

  • І ArrayList, і LinkedList є реалізацією інтерфейсу List.
  • Вони обидва підтримують порядок вставки елементів, що означає, відображаючи елементи ArrayList та LinkedList, набір результатів матиме той самий порядок, у якому елементи вставлені у Список.
  • Обидва ці класи не синхронізовані і можуть бути синхронізовані явно за допомогою методу Collections.synchronizedList.
  • iteratorІ listIteratorповертається цих класів fail-fast(якщо список конструктивно змінений в будь-який час після того, як ітератор створюється, яким - або чином , крім як через iterator’sвласний видаляти або додавати методи, итератор буде throwа ConcurrentModificationException).

Коли використовувати LinkedList і коли використовувати ArrayList?

  • Як пояснено вище вставки і видалення операцій дають хорошу продуктивність (O(1))в LinkedListпорівнянні з ArrayList(O(n)).

    Отже, якщо є необхідність частого додавання та видалення в додатку, тоді LinkedList - найкращий вибір.

  • get methodОперації пошуку ( ) швидко виконуються, Arraylist (O(1))але не єLinkedList (O(n))

    тож Якщо менше операцій додавання та видалення та більше вимог до пошукових операцій, найкраще ставитиметься ArrayList.


5

Операція get (i) у ArrayList швидша, ніж LinkedList, тому що:
ArrayList: реалізація змінної масиву реалізації інтерфейсу списку
LinkedList: змінної LinkedList двосторонніх переліків інтерфейсів List та Deque

Операції, що індексують список, перейдуть до списку від початку або до кінця, залежно від того, що ближче до зазначеного індексу.


5

1) Базова структура даних

Перша відмінність між ArrayList і LinkedList полягає в тому, що ArrayList підтримується Array, тоді як LinkedList підтримується LinkedList. Це призведе до подальших відмінностей у роботі.

2) LinkedList реалізує Deque

Ще одна відмінність між ArrayList і LinkedList полягає в тому, що крім інтерфейсу List, LinkedList також реалізує інтерфейс Deque, який забезпечує перші операції додавання () та опитування () та декілька інших функцій Deque. 3) Додавання елементів у ArrayList Додавання елемента в ArrayList - це операція O (1), якщо вона не викликає повторного розміру Array, у цьому випадку вона стає O (log (n)), з іншого боку, додаючи елемент у LinkedList - це операція O (1), оскільки вона не потребує навігації.

4) Видалення елемента з позиції

Для того, щоб вилучити елемент з певного індексу, наприклад, зателефонувавши на видалення (індекс), ArrayList виконує операцію копіювання, яка робить його близьким до O (n), тоді як LinkedList повинен перейти до тієї точки, яка також робить його O (n / 2) , оскільки він може переходити з будь-якого напрямку залежно від близькості.

5) Ітерація над ArrayList або LinkedList

Ітерація - це операція O (n) як для LinkedList, так і для ArrayList, де n - це число елемента.

6) Виведення елемента з позиції

Операція get (index) є O (1) в ArrayList, тоді як її O (n / 2) в LinkedList, оскільки їй потрібно пройти до цього запису. Хоча в позначенні Big O O (n / 2) - це просто O (n), оскільки ми ігноруємо там константи.

7) Пам'ять

LinkedList використовує об’єкт обгортки Entry, який є статичним вкладеним класом для зберігання даних та двох вузлів наступного та попереднього, тоді як ArrayList просто зберігає дані в Array.

Тому вимога пам'яті у випадку ArrayList здається меншою, ніж LinkedList, за винятком випадків, коли Array виконує операцію зміни розміру, коли копіює вміст з одного масиву в інший.

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

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

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

Іншими словами, вам не потрібно переходити через пов'язаний список, щоб дійти до позиції, куди потрібно додати елементи, у цьому випадку додавання стає операцією O (n). Наприклад, вставлення або видалення елемента в середині пов'язаного списку.

На мою думку, використовуйте ArrayList over LinkedList для більшості практичних цілей на Java.


1
Я думаю, що це найкраща заявлена ​​відповідь всієї групи тут. Це точно та інформативно. Я б запропонував змінити останній рядок - наприкінці додайте "осторонь черг", які є дуже важливими структурами, які насправді взагалі не мають сенсу для пов'язаного списку.
Білл К

3

Один із тестів, які я бачив тут, проводить тест лише один раз. Але я помітив, що вам потрібно запускати ці тести багато разів, і з часом їхні часи збігаються. В основному СВМ потрібно зігріти. Для мого конкретного випадку використання мені потрібно було додати / видалити елементи до списку, який зростає приблизно до 500 елементів. У моїх тестах LinkedListвиходило швидше, із LinkedListзаїздом близько 50 000 нс та ArrayListнадходженням біля 90 000 нс ... дай або візьми. Дивіться код нижче.

public static void main(String[] args) {
    List<Long> times = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        times.add(doIt());
    }
    System.out.println("avg = " + (times.stream().mapToLong(x -> x).average()));
}

static long doIt() {
    long start = System.nanoTime();
    List<Object> list = new LinkedList<>();
    //uncomment line below to test with ArrayList
    //list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        list.add(i);
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
    long end = System.nanoTime();
    long diff = end - start;
    //uncomment to see the JVM warmup and get faster for the first few iterations
    //System.out.println(diff)
    return diff;
}

2

І видалення (), і вставка () мають ефективність виконання O (n) як для ArrayLists, так і для LinkedLists. Однак причина лінійного часу обробки пов'язана з двома дуже різними причинами:

У ArrayList ви потрапляєте до елемента в O (1), але фактично видалення або вставлення чогось робить його O (n), оскільки всі наступні елементи потрібно змінити.

У LinkedList потрібно O (n), щоб насправді дістатись до потрібного елемента, оскільки ми повинні починати з самого початку, поки не досягнемо потрібного показника. Насправді видалення або вставлення є постійним, оскільки нам залишається лише змінити 1 посилання на delete () та 2 посилання на insert ().

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

Бонус: Хоча немає можливості зробити ці два методи O (1) для ArrayList, насправді існує спосіб зробити це в LinkedLists. Скажімо, ми хочемо пройти весь Список видалення та вставлення елементів на своєму шляху. Зазвичай ви починаєте з самого початку для кожного елемента, використовуючи LinkedList, ми також могли "зберегти" поточний елемент, над яким працюємо з Ітератором. За допомогою ітератора ми отримуємо ефективність O (1) для видалення () та вставки () під час роботи в LinkedList. Зробити це єдиною перевагою від продуктивності, я знаю, де LinkedList завжди кращий, ніж ArrayList.


1

ArrayList розширює AbstractList та реалізує інтерфейс списку. ArrayList - це динамічний масив.
Можна сказати, що він в основному створений для подолання недоліків масивів

Клас LinkedList розширює AbstractSequentialList та реалізує інтерфейс List, Deque та Queue.
Продуктивність
arraylist.get()становить O (1), тоді linkedlist.get()як O (n)
arraylist.add()є O (1) і linkedlist.add()0 (1)
arraylist.contains()є O (n) і linkedlist.contains()O (n)
arraylist.next()є O (1) і linkedlist.next()O (1)
arraylist.remove()є O (n) тоді linkedlist.remove()як O (1)
в масиві
iterator.remove()є O (n),
тоді як у пов'язаному списку
iterator.remove()є O (1)

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