Я всегда просто использовал:

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

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

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

3401
sdellysse 27 Ноя 2008 в 04:36
6
 – 
Hawkeye Parker
12 Окт 2016 в 06:58
6
Просто посмотрите цитату автора LinkedList stackoverflow.com/a/42529652/2032701, и вы получите практический смысл вопроса.
 – 
Ruslan
26 Дек 2017 в 22:17

27 ответов

До сих пор никто, кажется, не обращал внимания на объем памяти, занимаемый каждым из этих списков, кроме общего консенсуса о том, что 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)

656
Masterfego 12 Дек 2018 в 01:15
251
Проблема с вашей математикой в ​​том, что ваш график сильно преувеличивает влияние. Вы моделируете объекты, каждый из которых содержит только int, то есть 4 или 8 байтов данных. В связанном списке, по сути, есть 4 служебных слова. Таким образом, ваш график создает впечатление, что связанные списки используют «пять раз» хранилище списков массивов. Это не правильно. Накладные расходы составляют 16 или 32 байта на объект в качестве дополнительной настройки, а не коэффициента масштабирования.
 – 
Heath Hunnicutt
6 Сен 2013 в 04:18

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

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

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

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

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

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

146
lamont 1 Янв 2011 в 23:23
9
Разве другое решение не могло бы управлять размером списка программно, используя метод sureCapacity () ArrayList? У меня вопрос: почему так много вещей хранится в кучке хрупких структур данных, когда их лучше хранить в механизме кеширования или db? На днях у меня было интервью, в котором они ругались по поводу зла ArrayList, но я пришел сюда и обнаружил, что анализ сложности во всех отношениях лучше! ХОРОШО ОТЛИЧНЫЙ ВОПРОС ДЛЯ ОБСУЖДЕНИЯ. БЛАГОДАРНОСТЬ!
 – 
ingyhere
30 Окт 2012 в 09:33
24
как только вы получите java-приложения с кучей 10 ГБ, вы можете заблокировать приложение на 25 секунд во время полных GC, что вызывает таймауты На самом деле с LinkedList вы убиваете сборщик мусора во время полных GC, он должен повторять слишком большой LinkedList с отсутствием кеша на каждом узле.
 – 
bestsss
17 Дек 2014 в 23:15
6
Это ... ужасное решение. вы в основном полагаетесь на очистку сборщика мусора за вас, что невероятно дорого, когда вместо этого вы можете просто вызвать sureCapacity () для Arraylist ...
 – 
Philip Devine
19 Июн 2015 в 17:03
2
@Andreas: когда список увеличивается и уменьшается с шагом в один элемент, с ArrayList нет никаких проблем, поскольку для этого даже не требуется дополнительная память. Учитывая возможность того, что ArrayList должен увеличить свою емкость один раз в течение всего срока службы веб-службы в качестве причины для снижения производительности с помощью LinkedList, довольно странно, особенно, если вы сравните стоимость ввода-вывода веб-службы. Увеличение емкости ArrayList, даже с миллионами элементов, вряд ли будет замечено как задержка.
 – 
Holger
4 Июл 2016 в 13:20
6
@Andreas: LinkedList always выделяет в пять раз больше памяти, чем простой массив ссылок, поэтому ArrayList, временно требующий 2,5 раза, по-прежнему потребляет гораздо меньше памяти, даже если память не востребован. Поскольку выделение большого массива обходит пространство Eden, оно не влияет на поведение сборщика мусора, если действительно не хватает памяти, и в этом случае LinkedList взорвался намного раньше ...
 – 
Holger
5 Июл 2016 в 12:04

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

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

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

115
Daniel Martin 19 Май 2009 в 15:21
40
Начиная с Java 6 вы можете использовать ArrayDeque. docs.oracle.com/javase/6/docs/ api / java / util / ArrayDeque.html
 – 
Thomas Ahle
30 Дек 2011 в 19:01
1
ArrayDeque медленнее, чем LinkedList, если все операции не выполняются на одном конце. Это нормально, когда используется в качестве стека, но из этого не получается хорошая очередь.
 – 
Jeremy List
30 Апр 2015 в 05:12
2
Неправда - по крайней мере, для реализации Oracle в jdk1.7.0_60 и в следующем тесте. Я создал тест, в котором я зацикливаюсь на 10 миллионов раз, и у меня есть Deque из 10 миллионов случайных целых чисел. Внутри цикла я опрашиваю один элемент и предлагаю постоянный элемент. На моем компьютере LinkedList более чем в 10 раз медленнее, чем ArrayDeque, и использует меньше памяти). Причина в том, что в отличие от ArrayList, ArrayDeque хранит указатель на заголовок массива, поэтому ему не нужно перемещать все элементы при удалении заголовка.
 – 
Henno Vermeulen
22 Май 2015 в 13:18
7
ArrayDeque, вероятно, будет быстрее, чем Stack при использовании в качестве стека, и быстрее, чем LinkedList при использовании в качестве очереди.
 – 
akhil_mittal
17 Июн 2015 в 18:44
5
Обратите внимание, что комментарий akhil_mittal - это цитата из документации ArrayDeque.
 – 
Stuart Marks
29 Ноя 2015 в 01:47

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

Массив против ArrayList против LinkedList против вектора идет более подробно, как и Связанный список.

75
Tudor 9 Май 2020 в 05:38
4
Здесь стоит упомянуть, что LinkedList быстро добавляет / удаляет только первую и последнюю позиции - тогда сложность будет O (1), но добавление в середине все равно будет O (n), потому что нам нужно пробежать примерно n / 2 элементов LinkedList.
 – 
Dmitriy Fialkovskiy
10 Дек 2020 в 13:02

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

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

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

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

69
Ruslan 25 Апр 2017 в 13:23

Правильно или неверно: выполните тест локально и решите сами!

Редактирование / удаление выполняется быстрее в 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);
    }
}
56
Azeem 20 Апр 2018 в 09:23
1
Чтобы быть точным, ArrayList не нужно удваивать. Пожалуйста, сначала проверьте источники.
 – 
Danubian Sailor
6 Май 2013 в 18:40
Следует отметить, что ваш пример ошибочен ... Вы удаляете из строки между: 18 + [2, 12] байтов ("true0false", "true500000false"), в среднем 25 байтов, которые являются размерами элементов в центре. Известно, что при увеличении размера байта элемента связанный список работает лучше, а при увеличении размера списка лучше работает непрерывный массив (список). Что наиболее важно, вы выполняете .equals () для строк, что не является дешевой операцией. Если бы вы вместо этого использовали целые числа, я думаю, была бы разница.
 – 
Centril
21 Авг 2014 в 13:40
3
«... хуже в приложении большого объема »: это недоразумение. LinkedList имеет гораздо больше накладных расходов на память, потому что для каждого элемента существует объект узла с пятью полями. Во многих системах это составляет 20 байт накладных расходов. Средние накладные расходы памяти на элемент для ArrayList составляют полтора слова, что составляет 6 байтов, а в худшем случае - 8 байтов.
 – 
Lii
15 Сен 2015 в 12:53
1
Я сделал лучшую версию вашего теста здесь, с результатами - в приложении - конечная производительность arrayylist для вас искусственно занижена, потому что addAll дает массив хранения ТОЧНО начального размера, поэтому первая вставка всегда запускает копию массива. Кроме того, это включает в себя циклы прогрева, позволяющие выполнить JIT-компиляцию перед сбором данных.
 – 
BobMcGee
10 Мар 2016 в 21:48
4
Начиная с Java 8, вы можете использовать removeIf(element -> condition) там, где он подходит, что может быть значительно быстрее для ArrayList, по сравнению с циклом и удалением с помощью итератора, поскольку не требуется сдвигать весь остаток для каждого отдельного элемент. Будет ли это работать лучше или хуже, чем LinkedList, зависит от конкретного сценария, поскольку LinkedList теоретически составляет O (1), но удаление только одного узла требует нескольких обращений к памяти, которые могут легко превысить число необходимо для ArrayList при удалении значительного количества элементов.
 – 
Holger
14 Май 2019 в 20:05

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, элемент E)
    • добавить в указанную позицию
    • требуется смещение и возможная стоимость изменения размера памяти.
    • O (n)
  • удалить (индекс int)
    • удалить указанный элемент
    • требуется смещение и возможная стоимость изменения размера памяти.
    • O (n)
  • удалить (Объект o)
    • удалить первое вхождение указанного элемента из этого списка
    • сначала нужно найти элемент, а затем сдвиг и возможную стоимость изменения размера памяти.
    • O (n)

=== LinkedList ===

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

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

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

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

enter image description here

56
Premraj 27 Май 2018 в 13:29
3
«LinkedList быстрее, чем добавлять / удалять». Неправильно, проверьте ответ выше stackoverflow.com/a/7507740/638670
 – 
Abbas Gadhia
5 Ноя 2013 в 14:00

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


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

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

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

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

enter image description here

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

enter image description here

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

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

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

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

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

Ключевым моментом, который следует запомнить, является то, что стоимость выборки блока памяти более значительна, чем стоимость доступа к одной ячейке памяти. Вот почему считыватель 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 должен давать плохие или даже худшие результаты:

enter image description here

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

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


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

42
Lior Bar-On 19 Окт 2018 в 06:06
Я бы не назвал очередь уникальной или экстремальной! Очередь fifo намного проще реализовать в LinkedList, а не в ArrayList. На самом деле это кошмар для ArrayList, поскольку вам нужно отслеживать свой собственный запуск, остановку и выполнять собственное перераспределение, вы также можете использовать массив, но связанный список - это FIFO. Я не уверен в реализации Java, но LinkedList может выполнять O (1) как для операций очереди, так и для операций удаления из очереди (требуется специальный указатель на хвостовой элемент для удаления, который, как я полагаю, есть в java, но я не проверял дважды .)
 – 
Bill K
28 Дек 2018 в 21:26
2
При вставке в середину массива ArrayList используется собственный метод java.lang.System.arraycopy(), который написан на C ++ в OpenJDK. Таким образом, хотя теоретически LinkedList предстоит сделать меньше работы, на практике существует так много экстралингвистических механизмов, которые делают "Большое О" в значительной степени неуместным. В частности, насколько дружественны к кешу вещи в соответствии с этим отличным ответом.
 – 
simbo1905
20 Фев 2021 в 15:30

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

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

36
Azeem 20 Апр 2018 в 09:14
18
LinkedList не из дешевых для добавления элементов. Почти всегда быстрее добавить миллион элементов в ArrayList, чем добавить их в LinkedList. И большинство списков в реальном коде не состоят даже из миллиона элементов.
 – 
Porculus
10 Сен 2010 в 21:21
10
В любой момент вы знаете, сколько стоит добавить элемент в свой LinkedList. ArrayList у вас нет (в общем). Добавление одного элемента в список ArrayList, содержащий миллион элементов, может занять очень много времени - это операция O (n) плюс удвоение объема хранилища, если вы заранее не распределили пространство. Добавление элемента в LinkedList - O (1). Мое последнее заявление остается в силе.
 – 
Dustin
11 Сен 2010 в 03:03
4
Добавление одного элемента в ArrayList - это O (1), независимо от того, 1 миллион или 1 миллиард. Добавление элемента в LinkedList также является O (1). «Добавление» означает ДОБАВЛЕНИЕ ДО КОНЦА.
 – 
kachanov
13 Май 2012 в 18:36
Вы, должно быть, читали реализацию иначе, чем я. По моему опыту, копирование массива из 1 миллиарда элементов занимает больше времени, чем копирование массива из 1 миллиона элементов.
 – 
Dustin
14 Май 2012 в 09:57
6
Вы должны неправильно понять Дастина. Если вы не объявили массив из 1 миллиарда элементов, вам в конечном итоге потребуется изменить размер массива, и в этом случае вам нужно будет скопировать все элементы в новый массив большего размера, поэтому иногда вы получите O (N), однако со связанным списком вы всегда будете получить O (1)
 – 
Stan R.
19 Мар 2013 в 01:51

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

И, конечно же, Guava ImmutableList - ваш лучший друг.

25
Azeem 20 Апр 2018 в 09:16
3
Для небольших списков ArrayList.add (0) всегда будет быстрее, чем LinkedList.addFirst ().
 – 
Porculus
10 Сен 2010 в 21:20
1
Я постоянно слышу этот аргумент, что для небольших списков ArrayList.add (0) будет быстрее, насколько этот маленький? 10 элементов, 10 миллионов,?
 – 
garg10may
20 Сен 2016 в 15:07
1
Small меньше 10.
 – 
Jesse Wilson
21 Сен 2016 в 17:53
Small означает меньше, чем максимальная емкость внутреннего массива, лежащего в основе ArrayList.
 – 
Janac Meena
3 Мар 2020 в 20:07

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

22
Azeem 20 Апр 2018 в 09:29

Давайте сравним 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 не предоставляет доступ на основе индекса для своих элементов, поскольку он выполняет итерацию либо с начала, либо с конца (в зависимости от того, что ближе), чтобы получить узел по указанному индексу элемента.

  • операция insert () или add (Object)

    Вставки в LinkedList обычно выполняются быстрее, чем в ArrayList. В LinkedList добавление или вставка выполняется за O (1).

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

  • операция remove (int)

    Операция удаления в LinkedList обычно аналогична ArrayList, то есть O (n).

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

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


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

LinkedList можно перебирать в обратном направлении с помощью функции declndingIterator (), в то время как

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


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

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

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


5. Накладные расходы на память

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

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


Источник

21
Yoon5oo 24 Июл 2018 в 18:49

Вот нотация 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)

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

20
Azeem 20 Апр 2018 в 09:29
10
>>>> ArrayList add -> O (1) <- не правда. В некоторых случаях ArrayList придется увеличивать, чтобы добавить еще один элемент.
 – 
kachanov
8 Фев 2013 в 04:54
1
Удаление LinkedList не O (1), потребуется поиск элемента, который нужно удалить, поэтому в худшем случае O (n) и среднее значение O (n / 2)
 – 
garg10may
20 Сен 2016 в 15:05
Как и LinkedList.add(), хотя об этом говорится в большинстве ответов.
 – 
user207421
9 Окт 2019 в 22:56

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

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

15
Azeem 20 Апр 2018 в 09:15

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

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

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

11
Azeem 20 Апр 2018 в 09:14
2
Чтобы узнать больше не читайте, просто напишите код. и вы обнаружите, что реализация ArrayList быстрее, чем LinkedList при вставке и удалении.
 – 
kachanov
18 Май 2012 в 06:53

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

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

9
kemiller2002 20 Апр 2010 в 19:32

См. Руководства по Java - Список реализаций.

9
chharvey 5 Сен 2011 в 10:33
2
Привет @chharvey! Только ответы по ссылке получают 6 голосов за? Пожалуйста, добавьте несколько пунктов, которые могут поддерживать ссылку. Что, если oracle изменит свою ссылку?
 – 
user8898216
14 Ноя 2017 в 09:08

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

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

8
Community 23 Май 2017 в 15:02
2
Как так? Это может быть верно для структур данных связанных списков, но не для объекта Java LinkList. Вы не можете просто указать next из одного списка на первый узел во втором списке. Единственный способ - использовать addAll(), который добавляет элементы последовательно, хотя это лучше, чем цикл и вызов add() для каждого элемента. Чтобы сделать это быстро в O (1), вам понадобится класс компоновки (например, org.apache.commons.collections.collection.CompositeCollection), но тогда это будет работать для любого типа List / Collection.
 – 
Kevin Brock
23 Мар 2010 в 03:42
Да, верно. Я соответствующим образом отредактировал ответ. но посмотрите этот ответ, чтобы узнать, как это сделать с помощью LinkedList: stackoverflow.com/questions/2494031/…
 – 
Karussell
23 Мар 2010 в 15:47

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

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

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

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

8
Nesan Mano 21 Окт 2017 в 04:58

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

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

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

Еще одно различие между ArrayList и LinkedList заключается в том, что помимо интерфейса List, LinkedList также реализует интерфейс Deque, который обеспечивает операции «первым пришел - первым обслужен» для 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). Например, вставка или удаление элемента в середине связанного списка.

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

7
Wolfson 4 Сен 2020 в 19:53
1
Я думаю, что это лучший сформулированный ответ всей группы здесь. Это точно и информативно. Я бы предложил изменить последнюю строку - в конце добавить «помимо очередей», которые являются очень важными структурами, которые вообще не имеют смысла для связанного списка.
 – 
Bill K
28 Дек 2018 в 21:33

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

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

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

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

6
gaijinco 4 Окт 2011 в 03:23

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

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

5
L Joey 9 Ноя 2016 в 13:48

Один из тестов, которые я видел здесь, проводит тест только один раз. Но я заметил, что вам нужно запускать эти тесты много раз, и в конечном итоге их времена сойдутся. В основном JVM нужно разогреть. Для моего конкретного случая использования мне нужно было добавлять / удалять элементы в список, который вырастает примерно до 500 элементов. В моих тестах LinkedList вышел быстрее, с LinkedList около 50 000 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;
}
3
Jose Martinez 3 Мар 2020 в 21:11

И 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, мы также можем «сохранить» текущий элемент, над которым мы работаем, с помощью Iterator. С помощью итератора мы получаем эффективность O (1) для remove() и insert() при работе в LinkedList. Это единственное преимущество в производительности, которое я знаю, где LinkedList всегда лучше, чем ArrayList.

2
Wolfson 4 Сен 2020 в 21:04

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)
В Arraylist
iterator.remove() - O (n)
, а в связанном списке
iterator.remove() - O (1)

1
Randhawa 21 Фев 2018 в 00:51

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

|---------------------|---------------------|--------------------|------------|
|      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    |
|--------------------------------------|
23
Gayan Weerakutti 18 Фев 2019 в 10:08
1
ArrayDeque немного больше уравновешивает вещи с массивами, поскольку вставка / удаление передней / задней части - это все O (1), единственное, в чем по-прежнему выигрывает связанный список, - это добавление / удаление во время обхода (операции Iterator).
 – 
Bill K
21 Фев 2019 в 21:24