it-roy-ru.com

Когда использовать LinkedList поверх ArrayList в Java?

Я всегда был один, чтобы просто использовать:

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

Я использую интерфейс в качестве имени типа для portability, поэтому, когда я задаю такие вопросы, я могу переделать свой код. 

Когда LinkedList следует использовать поверх ArrayList и наоборот?

2750
sdellysse

SummaryArrayList с ArrayDeque предпочтительнее в много случаях использования, чем LinkedList. Если вы не уверены - просто начните с ArrayList.


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

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

Для LinkedList<E>

  • get(int index) is O(n) (в среднем n/4 шагов)
  • add(E element) is O(1)
  • add(int index, E element) is O(n) (с в среднем n/4 шагами), но O(1) когда index = 0 <--- главное преимущество LinkedList<E>
  • remove(int index) is O(n) (в среднем n/4 шагов)
  • Iterator.remove() is O(1). <--- главное преимущество LinkedList<E>
  • ListIterator.add(E element) is O(1) Это одно из основных преимуществ LinkedList<E>

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

Для ArrayList<E>

  • get(int index) is O(1) <--- главное преимущество ArrayList<E>
  • add(E element) является O(1) амортизированным, но O(n) наихудшим случаем, поскольку размер массива должен быть изменен и скопирован
  • add(int index, E element) is O(n)n/2 шагами в среднем)
  • remove(int index) is O(n)n/2 шагами в среднем)
  • Iterator.remove() is O(n)n/2 шагами в среднем)
  • ListIterator.add(E element) is 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(n) для ArrayList. Обратите внимание, что ArrayDeque может быть хорошей альтернативой LinkedList для добавления и удаления из головы, но это не List.

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

Начальная емкость ArrayList по умолчанию довольно мала (10 из Java 1.4 - 1.8). Но поскольку базовая реализация представляет собой массив, размер массива должен быть изменен, если вы добавляете много элементов. Чтобы избежать высокой стоимости изменения размера, когда вы знаете, что собираетесь добавить много элементов, создайте ArrayList с более высокой начальной емкостью.

3039
Jonathan Tran

До сих пор, похоже, никто не обращал внимания на объем памяти каждого из этих списков, кроме общего консенсуса о том, что LinkedList "намного больше", чем ArrayList, поэтому я сделал некоторые вычисления, чтобы продемонстрировать, насколько точно оба списка занимают N пустых ссылок ,.

Поскольку в их относительных системах ссылки бывают 32- или 64-битными (даже если они равны нулю), я включил 4 набора данных для 32- и 64-битных LinkedLists и ArrayLists.

Примечание: Размеры, показанные для строк ArrayList, предназначены для обрезанных списков - На практике емкость резервного массива в ArrayList обычно больше, чем его текущее количество элементов.

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


Graph of LinkedList and ArrayList No. of Elements x Bytes


Результат ясно показывает, что 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)

567
Numeron

ArrayList это то, что вы хотите. LinkedList почти всегда является ошибкой (производительности).

Почему LinkedList отстой:

  • Он использует множество небольших объектов памяти и, следовательно, влияет на производительность всего процесса.
  • Множество мелких объектов плохо влияют на локальность кэша.
  • Любая индексированная операция требует обхода, то есть имеет производительность O(n). Это неочевидно в исходном коде, что приводит к тому, что алгоритмы O(n) работают медленнее, чем при использовании ArrayList.
  • Получить хорошую производительность сложно.
  • Даже если производительность big-O такая же, как и у ArrayList, она все равно будет значительно медленнее.
  • Очень неприятно видеть LinkedList в источнике, потому что это, вероятно, неправильный выбор.
217
Tom Hawtin - tackline

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

Точно так же вы можете получить лучшую пропускную способность в приложении из стандартного сборщика мусора с пропускной способностью по умолчанию, но как только вы получите приложения Java с кучей 10 ГБ, вы можете заблокировать приложение на 25 секунд во время полных сборок мусора, что приводит к тайм-аутам и сбоям в SOA применяет и разрушает ваши SLA, если это происходит слишком часто. Несмотря на то, что сборщик CMS потребляет больше ресурсов и не достигает той же исходной пропускной способности, это гораздо лучший выбор, поскольку он имеет более предсказуемую и меньшую задержку.

ArrayList - лучший выбор для производительности, если все, что вы подразумеваете под производительностью, это пропускная способность и вы можете игнорировать задержки. По своему опыту работы я не могу игнорировать задержку в худшем случае.

132
lamont
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)

Алгоритмы: Big-Oh обозначения

ArrayLists хороши для записи один раз для чтения или добавления, но плохо подходят для добавления/удаления спереди или посередине.

117
Michael Munsey

Да, я знаю, это древний вопрос, но я добавлю два моих цента:

LinkedList является почти всегда неправильным выбором с точки зрения производительности. Существует несколько очень специфических алгоритмов, для которых требуется LinkedList, но они очень, очень редки, и алгоритм обычно будет конкретно зависеть от способности LinkedList относительно быстро вставлять и удалять элементы в середине списка, как только вы перейдете туда с ListIterator.

Существует один общий случай использования, в котором LinkedList превосходит ArrayList: это очереди. Однако, если ваша цель - производительность, вместо LinkedList вы также должны рассмотреть возможность использования ArrayBlockingQueue (если вы можете заранее определить верхнюю границу размера очереди и можете позволить себе выделить всю память заранее), или это Реализация CircularArrayList . (Да, это с 2001 года, поэтому вам нужно его обобщить, но я получил сопоставимые коэффициенты производительности с тем, что только что цитировалось в статье в недавней JVM)

97
Daniel Martin

Это вопрос эффективности. LinkedList быстр для добавления и удаления элементов, но медленный доступ к определенному элементу. ArrayList быстрый для доступа к определенному элементу, но может быть медленным, чтобы добавить к любому концу, и особенно медленным, чтобы удалить в середине.

Массив vs ArrayList vs LinkedList vs Vector более подробно, как и Linked List .

55
dgtized

Верно или неправильно: Пожалуйста, выполните тест на месте и решите сами!

Редактировать/Удалить быстрее в 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);
    }
}
52
Ash

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

get довольно понятен. O(1) для ArrayList, поскольку ArrayList разрешает произвольный доступ с использованием индекса. O(n) для LinkedList, потому что сначала нужно найти индекс. Примечание. Существуют разные версии add и remove

LinkedList быстрее в добавлении и удалении, но медленнее в получении. Вкратце, LinkedList должен быть предпочтительным, если: 

  1. нет большого количества произвольного доступа элемента 
  2. существует большое количество операций добавления/удаления

=== ArrayList ===

  • добавить (E e)
    • добавить в конце ArrayList
    • требуют изменения размера памяти. 
    • O (n) худший, O(1) амортизированный
  • add (int index, E element)
    • добавить к определенной позиции индекса
    • требуется сдвиг и возможная стоимость изменения размера памяти
    • O(n)
  • удалить (int index)
    • удалить указанный элемент
    • требуется сдвиг и возможная стоимость изменения размера памяти
    • O(n)
  • удалить (Объект o)
    • удалить первое вхождение указанного элемента из этого списка
    • сначала нужно найти элемент, а затем переместить и, возможно, изменить размер памяти
    • O(n)

=== LinkedList ===

  • добавить (E e)

    • добавить в конец списка
    • O(1)
  • добавить (индекс int, элемент E)

    • вставить в указанное положение
    • сначала нужно найти позицию
    • O(n)
  • удалить()
    • удалить первый элемент списка
    • O(1)
  • удалить (int index)
    • удалить элемент с указанным индексом
    • сначала нужно найти элемент
    • O(n)
  • удалить (Объект o)
    • удалить первое вхождение указанного элемента
    • сначала нужно найти элемент
    • O(n)

Вот рисунок из programcreek.com (add и remove - это первый тип, то есть добавить элемент в конец списка и удалить элемент в указанной позиции в списке.):

enter image description here

44
Ryan

1) Поиск: Операция поиска ArrayList довольно быстрая по сравнению с операцией поиска LinkedList. get (int index) в ArrayList дает производительность O(1), а LinkedList - O (n).

Причина: ArrayList поддерживает систему на основе индекса для своих элементов, поскольку она неявно использует структуру данных массива, что ускоряет поиск элемента в списке. С другой стороны, LinkedList реализует двусвязный список, который требует обхода всех элементов для поиска элемента.

2) Удаление: Операция удаления LinkedList дает производительность O(1), в то время как ArrayList обеспечивает производительность переменной: O(n) в худшем случае (при удалении первого элемента) и O(1) в лучшем случае (при удалении последнего элемента).

Вывод: Удаление элемента LinkedList происходит быстрее по сравнению с ArrayList.

Причина: Каждый из элементов LinkedList поддерживает два указателя (адреса), которые указывают на оба соседних элемента в списке. Следовательно, удаление требует только изменения местоположения указателя в двух соседних узлах (элементах) узла, который будет удален. В то время как в ArrayList все элементы должны быть сдвинуты, чтобы заполнить пространство, созданное удаленным элементом.

3) Результат вставок: Метод добавления LinkedList дает производительность O(1), в то время как ArrayList дает O(n) в худшем случае. Причина та же, что и для удаления.

4) Переполнение памяти: ArrayList поддерживает индексы и данные элемента, в то время как LinkedList поддерживает данные элемента и два указателя для соседних узлов, следовательно, потребление LinkedList сравнительно велико.

Есть немного сходств между этими классами, которые заключаются в следующем:

И ArrayList, и LinkedList являются реализациями интерфейса List . Они оба поддерживают порядок вставки элементов, что означает, что при отображении элементов ArrayList и LinkedList набор результатов будет иметь тот же порядок, в котором элементы были вставлены в List . Оба эти класса не синхронизированы и могут быть синхронизированы явно с использованием метода Collections.synchronizedList . Итератор и listIterator, возвращаемый этими классами, работают без сбоев (если список структурно изменяется в любое время после создания итератора в любом случае, кроме как через собственные методы удаления или добавления итератора, итератор генерирует исключение ConcurrentModificationException).

Когда использовать LinkedList и когда использовать ArrayList?

1) Как объяснено выше, операции вставки и удаления дают хорошую производительность (O (1)) в LinkedList по сравнению с ArrayList (O (n)). Следовательно, если существует требование частого добавления и удаления в приложении, то LinkedList является лучшим выбором.

2) Операции поиска (получения метода) выполняются быстро в ArrayList (O (1)), но не в LinkedList (O (n)), поэтому, если операций добавления и удаления меньше и требуется больше операций поиска, лучшим выбором будет ArrayList.

35
NoNaMe

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

Кто-нибудь на самом деле использует LinkedList? Я написал это, и я никогда не использую это.

Ссылка: https://Twitter.com/joshbloch/status/583813919019573248

Я прошу прощения за то, что я не настолько информативен, как другие ответы, но я подумал, что он будет самым интересным и не требующим пояснений.

33
Ruslan

ArrayList доступен произвольно, в то время как LinkedList действительно дешев для расширения и удаления элементов. В большинстве случаев ArrayList подходит.

Если вы не создали большие списки и не измерили узкое место, вам, вероятно, никогда не придется беспокоиться о разнице.

32
Dustin

Если в вашем коде есть add(0) и remove(0), используйте LinkedList, а его методы красивее addFirst() и removeFirst(). В противном случае используйте ArrayList.

И, конечно, Гуава 's ImmutableList ваш лучший друг.

20
Jesse Wilson

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

20
Ajax

Вот обозначение 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)

Исходя из этого, вы должны решить, что выбрать. :)

18
Rajith Delantha

Давайте сравним LinkedList и ArrayList w.r.t. ниже параметров:

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 есть два перегруженных метода удаления. один - это remove () без какого-либо параметра, который удаляет заголовок списка и работает в постоянном времени O (1). Другим перегруженным методом удаления в LinkedList является метод remove (int) или remove (Object), который удаляет Object или int, переданные в качестве параметра. Этот метод пересекает LinkedList до тех пор, пока не найдет объект и не отсоединит его от исходного списка. Следовательно, время выполнения этого метода O (n). 

    В то время как в ArrayList метод remove (int) включает в себя копирование элементов из старого массива в новый обновленный массив, следовательно, его время выполнения равно O (n).


3. Обратный Итератор

LinkedList может быть повторен в обратном направлении, используя downndingIterator (), в то время как 

в ArrayList нет функции downndingIterator (), поэтому нам нужно написать собственный код для перебора ArrayList в обратном направлении.


4. Начальная емкость

Если конструктор не перегружен, то ArrayList создает пустой список с начальной емкостью 10, а 

LinkedList создает только пустой список без какой-либо начальной емкости.


5. Объем памяти

Переполнение памяти в LinkedList больше по сравнению с ArrayList, поскольку узел в LinkedList должен поддерживать адреса следующего и предыдущего узла. В то время как 

В ArrayList каждый индекс содержит только фактический объект (данные).


Источник

14
Abhijeet Ashok Muneshwar

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


Теоретически, LinkedList имеет O(1) для add(E element)

Также добавление элемента в середине списка должно быть очень эффективным.

Практика совсем иная, так как LinkedList - это структура данных Cache Hostile. От производительности POV - очень мало случаев, когда LinkedList может быть лучше, чем Cache-friendlyArrayList.

Вот результаты тестового тестирования вставки элементов в случайных местах. Как видите, список массивов гораздо эффективнее, хотя в теории каждая вставка в середине списка потребует «переместить» n более поздние элементы массива (чем меньше значение, тем лучше):

 enter image description here

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

 enter image description here

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

Для этого есть две основные причины:

  1. Преимущественно - что узлы LinkedList случайно разбросаны по памяти. RAM («Память с произвольным доступом») на самом деле не случайная, и блоки памяти должны быть извлечены для кэширования. Эта операция занимает много времени, и когда такие выборки происходят часто - страницы памяти в кеше необходимо постоянно заменять -> Кэширование пропадает -> Кэширование неэффективно .ArrayList элементы хранятся в непрерывной памяти - что точно для чего оптимизируется современная архитектура процессора.

  2. SecondaryLinkedList требуется для удержания указателей назад/вперед, что означает 3-хкратное потребление памяти на хранимое значение по сравнению с ArrayList.

DynamicIntArray , кстати, является пользовательской реализацией ArrayList, содержащей Int (тип примитива), а не Objects - следовательно, все данные действительно хранятся смежно - следовательно, еще более эффективно.

Ключевым элементом для запоминания является то, что стоимость выборки блока памяти, является более значительной, чем стоимость доступа к одной ячейке памяти. Вот почему считыватель 1 МБ последовательной памяти работает в х400 раз быстрее, чем считывает этот объем данных из разных блоков памяти:

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 должен представлять плохие или даже худшие результаты:

 enter image description here

Примечание: это тест C++ Std lib, но мой предыдущий опыт показал, что результаты C++ и Java очень похожи. Исходный код

Копирование последовательного объема памяти - это операция, оптимизированная современными процессорами. Она меняет теорию и фактически делает ArrayList/Vector гораздо более эффективной.


Кредиты: Все опубликованные тесты созданы Kjell Hedström . Еще больше данных можно найти на его блог

12
Lior Bar-On

В дополнение к другим хорошим аргументам выше, вы должны заметить, что ArrayList реализует интерфейс RandomAccess, в то время как LinkedList реализует Queue.

Так или иначе, они решают несколько иные проблемы, с разницей в эффективности и поведении (см. Их список методов).

12
PhiLho
9
chharvey

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

Связанный список определяет переход от одного элемента к другому (Элемент a -> элемент b). Вы можете получить тот же эффект со списком массивов, но связанный список абсолютно говорит, какой элемент должен следовать за предыдущим. 

8
kemiller2002

Это зависит от того, какие операции вы будете делать больше в списке.

ArrayList быстрее получает доступ к индексированному значению. Это гораздо хуже при вставке или удалении объектов.

Чтобы узнать больше, прочитайте любую статью, в которой говорится о разнице между массивами и связанными списками.

7
Matthew Schinckel

Я прочитал ответы, но есть один сценарий, в котором я всегда использую LinkedList вместо ArrayList, которым я хочу поделиться, чтобы услышать мнения:

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

Мое обоснование состояло в том, что, поскольку невозможно точно знать, сколько результатов я получаю, память не будет потрачена впустую (как в ArrayList с разницей между емкостью и фактическим количеством элементов), и не будет потрачено впустую время, пытаясь продублируйте емкость.

Что касается ArrayList, я согласен, что по крайней мере вы всегда должны использовать конструктор с начальной емкостью, чтобы минимизировать дублирование массивов в максимально возможной степени.

6
gaijinco

Важной особенностью связанного списка (который я не прочитал в другом ответе) является объединение двух списков. Для массива это O(n) (+ накладные расходы на некоторые перераспределения), для связанного списка это только O(1) или O(2) ;-)

Важно: для Java это LinkedList это не так! Смотрите Есть ли быстрый метод concat для связанного списка в Java?

6
Karussell

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

|---------------------|---------------------|--------------------|------------|
|      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    |
|--------------------------------------|
5
Gayan Weerakutti

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

ArrayList против 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 метод add обеспечивает производительность 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 a ConcurrentModificationException).

Когда использовать LinkedList, а когда использовать ArrayList?

  • Как объяснено выше, операции вставки и удаления дают хорошую производительность (O(1)) в LinkedList по сравнению с ArrayList(O(n)).

    Следовательно, если есть требование частого добавления и удаления в приложении, тогда LinkedList является лучшим выбором.

  • Операции поиска (get method) выполняются быстро в Arraylist (O(1)), но не в LinkedList (O(n))

    так что если будет меньше операций добавления и удаления и больше требований к поисковым операциям, ArrayList будет вашим лучшим выбором.

5
Real73

Операция get (i) в ArrayList выполняется быстрее, чем LinkedList, потому что:
ArrayList: Реализация Resizable-массива интерфейса List
LinkedList: Реализация двусвязного списка интерфейсов List и Deque

Операции, которые индексируют в списке, будут проходить по списку с начала или конца, в зависимости от того, что ближе к указанному индексу. 

5
Amitābha

ArrayList и LinkedList имеют свои плюсы и минусы.

ArrayList использует непрерывный адрес памяти по сравнению с LinkedList, который использует указатели на следующий узел. Поэтому, когда вы хотите найти элемент в ArrayList, это быстрее, чем делать n итераций с LinkedList.

С другой стороны, вставка и удаление в LinkedList намного проще, потому что вам просто нужно изменить указатели, тогда как ArrayList подразумевает использование операции сдвига для любой вставки или удаления.

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

5
Nesan Mano

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

Первое различие между ArrayList и LinkedList связано с тем, что ArrayList поддерживается Array, а LinkedList поддерживается LinkedList. Это приведет к дальнейшим различиям в производительности.

2) LinkedList реализует Deque  

Другое отличие между ArrayList и LinkedList состоит в том, что помимо интерфейса List, LinkedList также реализует интерфейс Deque, который обеспечивает операции first-first-out для add () и poll () и некоторых других функций Deque. 3) Добавление элементов в ArrayList Добавление элемента в ArrayList - это операция O(1), если она не запускает изменение размера массива, в этом случае оно становится O (log (n)), с другой стороны, добавление элемента в LinkedList является операцией O(1), так как не требует никакой навигации.

4) Удаление элемента из позиции  

Чтобы удалить элемент из определенного индекса, например, вызывая remove (index), 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 просто хранит данные в массиве.

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

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

Из всех вышеупомянутых различий между ArrayList и LinkedList, похоже, что ArrayList - лучший выбор, чем LinkedList, почти во всех случаях, кроме случаев, когда вы выполняете частую операцию add (), а не remove () или get ().

Связанный список легче изменить, чем ArrayList, особенно если вы добавляете или удаляете элементы из начала или конца, потому что связанный список внутренне хранит ссылки на эти позиции, и они доступны во время O(1).

Другими словами, вам не нужно проходить через связанный список, чтобы достичь позиции, в которую вы хотите добавить элементы, в этом случае добавление становится операцией O(n). Например, вставка или удаление элемента в середине связанного списка.

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

3
Anjali Suman

Оба метода remove () и insert () имеют эффективность выполнения O(n) для ArrayLists и LinkedLists. Однако причина времени линейной обработки обусловлена ​​двумя очень разными причинами:

В ArrayList вы попадаете в элемент в O (1), но на самом деле удаление или вставка чего-либо делает его O(n), потому что все следующие элементы должны быть изменены.

В LinkedList требуется O(n), чтобы фактически добраться до нужного элемента, потому что мы должны начинать с самого начала, пока не достигнем желаемого индекса. На самом деле удаление или вставка является константой, потому что нам нужно изменить только 1 ссылку для remove () и 2 ссылки для insert ().

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

Бонус: хотя нет никакого способа сделать эти два метода O(1) для ArrayList, на самом деле есть способ сделать это в LinkedLists. Допустим, мы хотим пройтись по всему списку, удаляя и вставляя элементы на нашем пути. Обычно вы начинаете с самого начала для каждого элемента, используя LinkedList, мы также можем «сохранить» текущий элемент, над которым мы работаем, с помощью итератора. С помощью Итератора мы получаем эффективность O(1) для remove () и insert () при работе в LinkedList. Делая это единственным преимуществом в производительности, я знаю, что LinkedList всегда лучше, чем ArrayList.

1
pietz

ArrayList расширяет AbstractList и реализует интерфейс List. 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() is O (n)
тогда как в связанном списке 
iterator.remove()is O(1) 

0
Randhawa

Один из тестов, которые я видел здесь, проводит тест только один раз. Но что я заметил, так это то, что вам нужно запускать эти тесты много раз, и в конечном итоге их время будет сходиться. По сути, JVM нужно разогреть. Для моего конкретного случая использования мне нужно было добавить/удалить элементы до последней, которая увеличивается примерно до 500 элементов. В моих тестах LinkedList выходил быстрее, со связанными LinkedList, входящими примерно в 50000 NS, и ArrayList, входящими примерно в 90,000 NS ... давай или бери. Смотрите код ниже.

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;
}
0
Jose Martinez