Чому ArrayDeque кращий за LinkedList


161

Я намагаюся зрозуміти, чому Java ArrayDeque кращий, ніж Java LinkedList, оскільки вони реалізують інтерфейс Deque.

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

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


5
Подивіться на відповідь в цьому питанні я зробив днів тому: stackoverflow.com/questions/6129805 / ...
Ренато Dinhani

1
У папці, де встановлено jdk, є файл src.zip. Це архів із вихідним кодом класів java. Настійно рекомендую вивчити структуру та внутрішні класи цих класів, щоб краще зрозуміти, як працюють класи Java.

Відповіді:


155

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

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

Єдиною кращою роботою пов'язаного списку є видалення поточного елемента під час ітерації.


57
Ще одна різниця, яку слід пам’ятати: LinkedList підтримує нульові елементи, тоді як ArrayDeque цього не робить.
Люк Ушервуд

15
Також ще одним невеликим недоліком (для додатків у режимі реального часу) є те, що для операції push / add потрібно трохи більше, коли внутрішній масив ArrayDeque заповнений, оскільки він повинен подвоїти свій розмір та скопіювати всі дані.
Андрій І

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

3
@DavidT. b / c, це передбачає витрати GC на звільнений вузол, присвоєння головки може також вимагати маркування картки (для GC знову, якщо LinkedList вже в штаті генерації) ... і це на додаток до додаткової непрямості (кеш- miss) для повернення елемента та повторного посилання.
bestsss

1
Крім того, LinkedListреалізує, Listпоки ArrayDequeні. Це означає, що LinkedListтаких методів, як indexOfі remove(int)поки ArrayDequeнемає, немає. Іноді це може бути важливо.
ЖекаКозлов

60

Я вважаю, що головне вузьке місце LinkedListполягає в тому, що щоразу, коли ви натискаєте на будь-який кінець деки, за сценою реалізація виділяє новий зв'язаний вузол списку, який по суті включає JVM / OS, і це дорого. Крім того, щоразу, коли ви з'являєтесь з будь-якого кінця, внутрішні вузли LinkedListстають придатними для вивезення сміття, і це більше роботи поза сценою. Крім того, оскільки вузли пов'язаного списку виділяються тут і там, використання кешу CPU не принесе великої користі.

Якщо це може бути цікаво, у мене є доказ , що додавання (додавання) елемент до ArrayListабо ArrayDequeпрацюють в амортизаційному постійна час; посилайтеся на це .


26

ArrayDeque є новим для Java 6, тому багато коду (особливо проекти, які намагаються бути сумісними з більш ранніми версіями Java) не використовують його.

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


17

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

Але, це не означає, я б сліпо приймати LinkedList«або s ArrayDeque» s сторона. Якщо ви хочете знати, подивіться на нижченаведений показник, зроблений Брайаном .

Тестова установка враховує:

  • Кожен тестовий об'єкт є рядком 500 символів. Кожна струна - це різний об'єкт у пам'яті.
  • Розмір тестового масиву буде змінюватися під час тестів.
  • Для кожного комбінації розміру масиву / черги-реалізація виконується 100 тестів та обчислюється середній час за тест.
  • Кожен тест складається із заповнення кожної черги усіма об'єктами, а потім видалення їх усіх.
  • Виміряйте час у мілісекундах.

Результати тесту:

  • Нижче 10 000 елементів тести LinkedList та ArrayDeque усереднюються на рівні нижче 1 мс.
  • У міру збільшення наборів даних різниця між середнім часом тестування ArrayDeque та LinkedList збільшується.
  • При тестовому розмірі 9 900 000 елементів підхід LinkedList зайняв ~ 165% довше, ніж підхід ArrayDeque.

Графік:

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

Винос:

  • Якщо ваша вимога зберігає 100 або 200 елементів, це не має великого значення, використовуючи будь-яку з черг.
  • Однак, якщо ви розвиваєтеся на мобільних пристроях, можливо, ви хочете скористатись максимальною ємністю ArrayListабо, ArrayDequeмаючи на увазі, максимальну ємність, яку може знадобитися у списку через суворе обмеження пам'яті.
  • Існує дуже багато коду, написаного LinkedListнастільки ретельно, протестуючи, ArrayDequeособливо коли він не використовує Listінтерфейс (я думаю, це причина досить велика). Можливо, ваша база коду широко розмовляє із списком інтерфейсу, швидше за все, і ви вирішите зайти з ArrayDeque. Використання його для внутрішніх реалізацій може бути хорошою ідеєю ...

Як би цей орієнтир фіксував час GC, спричинене сміттям пов'язаного списку?
0xbe5077ed

3

ArrayDeque і LinkedList реалізують Deque інтерфейс , але реалізація відрізняється.

Основні відмінності:

  1. ArrayDeque класом є змінною реалізацією масиву з Deque інтерфейсу і LinkedList клас є реалізацією списку

  2. Елементи NULL можуть бути додані до LinkedList, але не в ArrayDeque

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

  4. LinkedList реалізація споживає більше пам'яті , ніж ArrayDeque

Тож якщо вам не потрібно підтримувати елементи NULL і & шукати менше пам’яті та ефективність додавання / видалення елементів на обох кінцях, ArrayDeque є найкращим

Детальнішу інформацію див. У документації .


13
"У пов'язаному списку знадобиться O (N), щоб знайти останній елемент." не зовсім так. LinkedList реалізований у вигляді подвійно пов'язаного списку, тому вам не доведеться обходити список, щоб отримати останній елемент ( header.previous.element). Заяву на "ефективність пам'яті" також можна оскаржити, оскільки резервний масив завжди змінюється до наступної потужності 2.
Clément MATHIEU

5
"Знадобиться O (N), щоб знайти останній елемент" WRONG. Зв'язаний список зберігає посилання на останній вузол і LinkedList.descendingIterator () отримує цей вузол. Таким чином, ми отримуємо O (1) продуктивність. Дивіться: coffeeorientedprogramming.wordpress.com/2018/04/23/… (таким чином, зволікаючи).
Лев Уфімцев

1
Коли ви використовуєте a Iteratorдля доступу до останнього елемента, операція O (N) для обох класів. Коли ви використовуєте загальний Dequeінтерфейс, доступ до останнього елемента є O (1) для обох класів. Незалежно від того, до якої точки зору ви займаєтесь, віднесення одночасно O (1) до ArrayDequeO (N) LinkedListє помилковим.
Хольгер

Усі ваші пропозиції приймаються та виправляють вміст
Равіндра бабу,

1

хоча ArrayDeque<E>і LinkedList<E>обидва реалізували Deque<E>інтерфейс, але ArrayDeque використовує в основному масив Object E[]для зберігання елементів всередині свого Object, тому він, як правило, використовує індекс для розміщення елементів голови та хвоста.

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


-1

Це не завжди так.

Наприклад, у нижченаведеному випадку linkedlistмає кращі показники, ніж ArrayDequeзгідно з leetcode 103.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> rs=new ArrayList<>();
        if(root==null)
            return rs;
        // 👇 here ,linkedlist works better
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        boolean left2right=true;
        while(!queue.isEmpty())
        {
            int size=queue.size();
            LinkedList<Integer> t=new LinkedList<>();
            while(size-->0)
            {
                TreeNode tree=queue.remove();
                if(left2right)  
                    t.add(tree.val);
                else
                    t.addFirst(tree.val);
                if(tree.left!=null)
                {
                    queue.add(tree.left);
                }
                if(tree.right!=null)
                {
                    queue.add(tree.right);
                }
            }
            rs.add(t);
            left2right=!left2right;
        }
        return rs;
    }
}

-5

Часова складність для ArrayDeque для доступу до елемента - O (1), а для LinkList - O (N) для доступу до останнього елемента. ArrayDeque не є безпечним для потоків, тому необхідна синхронізація вручну, щоб ви могли отримати доступ до нього через декілька потоків, щоб вони були швидшими.


3
Якщо ви посилаєтесь на LinkedListJava Collection, він є подвійним зв'язком і має швидкий доступ до голови та хвоста, тому доступ до останнього елемента також займає O (1).
Моріс

Доступ до останнього елемента в LinkedList не є O (N). Якщо ви використовуєте descendingIterator (), він виконується в O (1). Дивіться сторінку Coffeeorientedprogramming.wordpress.com/2018/04/23/… (таким чином, зворотній зв'язок).
Лев Уфімцев

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