Quando usar o LinkedList sobre o ArrayList em Java?

3122

Eu sempre fui o único a usar:

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

Uso a interface como o nome do tipo para portabilidade , para que, quando fizer perguntas como essas, possa refazer o meu código.

Quando deve LinkedListser utilizado ArrayListe vice-versa?

sdellysse
fonte
4
Veja também: Matriz versus lista vinculada
Hawkeye Parker
1
Basta ver a citação do autor do LinkedList stackoverflow.com/a/42529652/2032701 e você terá uma noção prática do problema.
Ruslan #
Consulte a publicação Java LinkedList vs ArrayList , que descreve as diferenças e inclui alguns testes de desempenho.
alonana

Respostas:

3371

Resumo ArrayList com ArrayDequeé preferível em muitos mais casos de uso do que LinkedList. Se você não tiver certeza, basta começar ArrayList.


LinkedListe ArrayListsão duas implementações diferentes da interface da lista. LinkedListimplementa-o com uma lista duplamente vinculada. ArrayListimplementa-o com uma matriz de redimensionamento dinâmico.

Como nas operações de lista e matriz vinculadas padrão, os vários métodos terão tempos de execução algorítmicos diferentes.

Para LinkedList<E>

  • get(int index)é O (n) (com n / 4 etapas em média), mas O (1) quando index = 0ou index = list.size() - 1(nesse caso, você também pode usar getFirst()e getLast()). Um dos principais benefícios de LinkedList<E>
  • add(int index, E element)é O (n) (com n / 4 etapas em média), mas O (1) quando index = 0ou index = list.size() - 1(nesse caso, você também pode usar addFirst()e addLast()/ add()). Um dos principais benefícios de LinkedList<E>
  • remove(int index)é O (n) (com n / 4 etapas em média), mas O (1) quando index = 0ou index = list.size() - 1(nesse caso, você também pode usar removeFirst()e removeLast()). Um dos principais benefícios de LinkedList<E>
  • Iterator.remove()é O (1) . Um dos principais benefícios de LinkedList<E>
  • ListIterator.add(E element)é O (1) . Um dos principais benefícios de LinkedList<E>

Nota: Muitas das operações precisam de n / 4 etapas, em média, número constante de etapas no melhor caso (por exemplo, índice = 0) e n / 2 etapas no pior caso (meio da lista)

Para ArrayList<E>

  • get(int index)é O (1) . Principal benefício de ArrayList<E>
  • add(E element)é O (1) amortizado, mas O (n) na pior das hipóteses, pois a matriz deve ser redimensionada e copiada
  • add(int index, E element)é O (n) (com n / 2 passos em média)
  • remove(int index)é O (n) (com n / 2 passos em média)
  • Iterator.remove()é O (n) (com n / 2 passos em média)
  • ListIterator.add(E element)é O (n) (com n / 2 passos em média)

Nota: Muitas das operações precisam de n / 2 etapas, em média, número constante de etapas no melhor caso (final da lista), n etapas no pior caso (início da lista)

LinkedList<E>permite inserções ou remoções em tempo constante usando iteradores , mas apenas acesso seqüencial aos elementos. Em outras palavras, você pode percorrer a lista para frente ou para trás, mas encontrar uma posição na lista leva um tempo proporcional ao tamanho da lista. Javadoc diz que "as operações indexadas na lista percorrerão a lista do começo ou do fim, o que estiver mais próximo" , então esses métodos são O (n) ( n / 4 passos) em média, embora O (1) para index = 0.

ArrayList<E>, por outro lado, permita acesso rápido de leitura aleatória, para que você possa pegar qualquer elemento em tempo constante. Mas adicionar ou remover de qualquer lugar, exceto o final, exige a troca de todos os últimos elementos, seja para fazer uma abertura ou preencher a lacuna. Além disso, se você adicionar mais elementos do que a capacidade da matriz subjacente, uma nova matriz (1,5 vezes o tamanho) será alocada e a matriz antiga será copiada para a nova. Portanto, adicionar a um ArrayListé O (n) na pior das hipóteses. caso, mas constante, em média.

Portanto, dependendo das operações que você pretende executar, escolha as implementações adequadamente. A iteração sobre qualquer tipo de lista é praticamente igualmente barata. (Iterar sobre um ArrayListé tecnicamente mais rápido, mas, a menos que você esteja fazendo algo realmente sensível ao desempenho, não se preocupe com isso - as duas são constantes.)

Os principais benefícios do uso de um LinkedListsurgem quando você reutiliza os iteradores existentes para inserir e remover elementos. Essas operações podem ser realizadas em O (1) , alterando apenas a lista localmente. Em uma lista de matrizes, o restante da matriz precisa ser movido (ou seja, copiado). Por outro lado, procurar de uma LinkedListmaneira seguir os links em O (n) ( n / 2 passos) para o pior caso, enquanto que em uma ArrayListposição desejada pode ser computado matematicamente e acessado em O (1) .

Outro benefício do uso de a LinkedListsurge quando você adiciona ou remove do cabeçalho da lista, pois essas operações são O (1) , enquanto são O (n) para ArrayList. Observe que ArrayDequepode ser uma boa alternativa LinkedListpara adicionar e remover da cabeça, mas não é a List.

Além disso, se você tiver listas grandes, lembre-se de que o uso de memória também é diferente. Cada elemento de a LinkedListtem mais sobrecarga, já que os ponteiros para os elementos seguinte e anterior também são armazenados. ArrayListsnão tem essa sobrecarga. No entanto, ArrayListsocupe a quantidade de memória alocada para a capacidade, independentemente de os elementos terem sido realmente adicionados.

A capacidade inicial padrão de um ArrayListé bem pequena (10 de Java 1.4 - 1.8). Mas como a implementação subjacente é uma matriz, ela deverá ser redimensionada se você adicionar muitos elementos. Para evitar o alto custo de redimensionamento quando você sabe que irá adicionar muitos elementos, construa-o ArrayListcom uma capacidade inicial mais alta.

Jonathan Tran
fonte
182
Esqueceu de mencionar os custos de inserção. Em uma LinkedList, depois de ter a posição correta, a inserção custa O (1), enquanto em uma ArrayList vai para O (n) - todos os elementos após o ponto de inserção devem ser movidos.
David Rodríguez - dribeas 27/11/08
26
Em relação ao uso do Vector: Realmente não há necessidade de recorrer ao Vector. A maneira de fazer isso é com a implementação preferida da lista e uma chamada para synchronizedList para fornecer um wrapper sincronizado. Veja: java.sun.com/docs/books/tutorial/collections/implementations/…
Ryan Cox
69
Não, para um LinkedList, get ainda é O (n), mesmo que você conheça a posição, porque, para chegar a essa posição, a implementação subjacente precisa percorrer os "próximos" ponteiros da lista vinculada para chegar ao valor dessa posição. Não existe acesso aleatório. Para a posição 2, andar pelos ponteiros pode ser barato, mas para a posição 1 milhão, não tão barato. O ponto é que é proporcional à posição, o que significa que é O (n).
Jonathan Tran
53
@ Kevin Pode importar que a memória esteja "próxima". O hardware armazenará em cache blocos de memória contíguos (RAM dinâmica) em RAM estática mais rápida no cache L1 ou L2. Na teoria e na maioria das vezes praticamente, a memória pode ser tratada como acesso aleatório. Mas, na realidade, a leitura seqüencial da memória será um pouco mais rápida do que em ordem aleatória. Para um loop crítico de desempenho, isso pode importar. Eles chamam de "local espacial" ou localidade de referência .
Jonathan Tran
92
Não existe algo como O(n/2)ou O(n/4). A notação O grande mostra como uma operação é escalada com n maior . e uma operação que precisa de n/2etapas é dimensionada exatamente como uma operação que precisa de netapas, e é por isso que os somas ou fatores constantes são removidos. O(n/2)e O(n/4)são ambos justos O(n). LinkedListe ArrayListtêm diferentes fatores constantes de qualquer maneira, portanto, não faria sentido comparar um O(n/2)de um com O(n/4)outro, ambos apenas denotam operações de dimensionamento linear.
Holger
630

Até agora, ninguém parece ter abordado a área de memória de cada uma dessas listas, além do consenso geral de que a LinkedListé "muito mais" do que uma, ArrayListentão fiz um processamento de números para demonstrar exatamente quanto ambas as listas ocupam para N referências nulas.

Como as referências são de 32 ou 64 bits (mesmo quando nulas) em seus sistemas relativos, incluí 4 conjuntos de dados para 32 e 64 bits LinkedListse ArrayLists.

Nota: Os tamanhos mostrados para as ArrayListlinhas são para listas aparadas - Na prática, a capacidade da matriz de apoio em uma ArrayListé geralmente maior que a contagem atual de elementos.

Nota 2: (obrigado BeeOnRope) Como o CompressedOops agora é padrão a partir do meio do JDK6 e acima, os valores abaixo para máquinas de 64 bits basicamente correspondem aos seus equivalentes de 32 bits, a menos que você o desative especificamente.


Gráfico de LinkedList e ArrayList Nº de elementos x bytes


O resultado mostra claramente que LinkedListé muito mais do que isso ArrayList, especialmente com uma contagem muito alta de elementos. Se a memória é um fator, evite LinkedLists.

As fórmulas que usei seguem, deixe-me saber se fiz algo errado e vou consertar. 'b' é 4 ou 8 para sistemas de 32 ou 64 bits e 'n' é o número de elementos. Observe que a razão para os mods é porque todos os objetos em java ocupam um espaço múltiplo de 8 bytes, independentemente de todos serem usados ​​ou não.

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)

Numeron
fonte
2
Muito interessante ver que o LinkedList requer tanta memória quanto o ArrayList para armazenar um único elemento. Que não intuitivo! O que acontece se você executar o seu exemplo com -XX: + UseCompressedOops?
precisa saber é o seguinte
215
O problema com sua matemática é que seu gráfico exagera bastante o impacto. Você está modelando objetos, cada um contendo apenas um int, portanto, 4 ou 8 bytes de dados. Na lista vinculada, existem essencialmente 4 "palavras" de sobrecarga. Assim, seu gráfico dá a impressão de que listas vinculadas usam "cinco vezes" o armazenamento de listas de matrizes. Isto está errado. A sobrecarga é de 16 ou 32 bytes por objeto, como um ajuste aditivo, não como um fator de escala.
Heath Hunnicutt
6
Nenhum dos objetos ArrayList / LinkedList / Node contém apenas um int, então não entendo o que você está dizendo lá. Eu reformulei 'sobrecarga de objeto' para 'cabeçalho de objeto' para esclarecer - existe um cabeçalho de 8 bytes para cada objeto, independentemente do sistema, e sim, que inclui todos os objetos Node no LinkedList, que são contados corretamente, tanto quanto possível contar. Aliás, ao analisá-lo novamente, encontrei alguns outros problemas com minha matemática no LinkedList, o que na verdade torna a divisão e o ArrayList ainda piores . Fico feliz em atualizá-lo, por isso não hesite em esclarecer e elaborar mais detalhes.
Numeron 17/10/2013
6
Note-se que CompressedOopsagora é padrão em todos os JDKs recentes (7, 8 e atualizações de 6 por alguns anos), portanto, 64 bits não fará diferença ArrayListou LinkedListtamanhos, a menos que você tenha desabilitado explicitamente oops compactados para alguma razão.
BeeOnRope
1
@jontejj, o aumento de capacidade padrão é de 50%; portanto, quando você preencher um ArrayListsem especificar uma capacidade inicial, ele ainda usará significativamente menos memória que um LinkedList.
21919 Holger
244

ArrayListé o que você quer. LinkedListquase sempre é um bug (desempenho).

Por que LinkedListé péssimo:

  • Ele usa muitos objetos de memória pequenos e, portanto, afeta o desempenho em todo o processo.
  • Muitos objetos pequenos são ruins para a localidade do cache.
  • Qualquer operação indexada requer uma travessia, ou seja, possui desempenho O (n). Isso não é óbvio no código-fonte, levando a algoritmos O (n) mais lentos do que se ArrayListfosse usado.
  • Obter um bom desempenho é complicado.
  • Mesmo quando o desempenho do grande O é o mesmo ArrayList, provavelmente será significativamente mais lento.
  • É chocante ver LinkedListna fonte, porque provavelmente é a escolha errada.
Tom Hawtin - linha de orientação
fonte
237
Desculpa. marcou você para baixo. LinkedList não é uma merda. Há situações em que o LinkedList é a classe correta a ser usada. Concordo que não há muitas situações em que é melhor que uma lista de matrizes, mas elas existem. Eduque pessoas que fazem coisas tolas!
David Turner
40
Lamento ver que você recebeu muitos votos negativos por isso. De fato, há muito poucas razões para usar o LinkedList do Java. Além do mau desempenho, ele também usa muito mais memória do que as outras classes concretas de List (cada nó tem dois ponteiros adicionais e cada nó é um objeto de invólucro separado com os bytes de sobrecarga extras que os acompanham).
Kevin Brock
42
Esta é uma das respostas mais úteis aqui. É uma pena que muitos programadores não compreendam (a) a diferença entre tipos de dados abstratos e implementações concretas e (b) a importância do mundo real de fatores constantes e sobrecarga de memória na determinação do desempenho.
Porculus 10/09/10
50
-1: esta é uma visão bastante intermitente. Sim, é verdade que o ArrayList é uma ferramenta muito versátil. No entanto, tem suas limitações. Há casos em que isso causará problemas e você precisará usar o LinkedList. Obviamente, é uma solução muito especializada e, como qualquer ferramenta especializada, na maioria dos casos, é superada por uma ferramenta versátil. Mas isso não significa que seja "péssimo" ou algo assim, você só precisa saber quando usá-lo.
Malcolm
27
@ DavidTurner: Eles existem, mas acho que o ponto de Tom é que, se você precisar perguntar, provavelmente deseja ArrayList.
user541686
139

Como alguém que faz engenharia de desempenho operacional em serviços da Web SOA em larga escala há cerca de uma década, eu preferiria o comportamento do LinkedList ao invés de ArrayList. Embora a taxa de transferência no estado estacionário do LinkedList seja pior e, portanto, possa levar à compra de mais hardware - o comportamento do ArrayList sob pressão pode levar os aplicativos em um cluster a expandir suas matrizes em quase sincronicidade e, para tamanhos de matriz grandes, pode levar à falta de capacidade de resposta no aplicativo e uma interrupção, enquanto estiver sob pressão, que é um comportamento catastrófico.

Da mesma forma, você pode obter uma melhor taxa de transferência em um aplicativo a partir do coletor de lixo garantido de taxa de transferência padrão, mas uma vez que você obtém aplicativos java com 10 GB de pilha, pode encerrar o aplicativo por 25 segundos durante um GC completo, causando timeouts e falhas em aplicativos SOA e altera seus SLAs se ocorrer com muita frequência. Embora o coletor do CMS consiga mais recursos e não atinja a mesma taxa de transferência bruta, é uma escolha muito melhor, pois possui latência mais previsível e menor.

ArrayList é apenas uma opção melhor para desempenho se tudo o que você quer dizer com desempenho é taxa de transferência e você pode ignorar a latência. Na minha experiência no trabalho, não posso ignorar a pior latência.

Lamont
fonte
8
Outra solução não estaria gerenciando o tamanho da lista de forma programática, usando o método assegCapacity () do ArrayList? Minha pergunta é por que tantas coisas estão sendo armazenadas em um monte de estruturas de dados frágeis quando elas podem ser melhor armazenadas em um mecanismo de cache ou db? Eu tive uma entrevista outro dia em que eles xingaram de um lado para o outro sobre os males do ArrayList, mas eu venho aqui e acho que a análise da complexidade é melhor em tudo! GRANDE PONTO PARA DISCUSSÃO, PENSAMENTO. OBRIGADO!
ingyhere
22
depois que você obtém aplicativos java com 10 GB de pilha, pode encerrar o aplicativo por 25 segundos durante um GC completo, o que causa tempos limite Na verdade, com o LinkedList você mata o coletor de lixo durante o GC completo, ele precisa iterar o LinkedList muito grande com falta de cache cada nó.
bestsss
5
Essa é ... uma solução horrível. você está basicamente dependente do GC limpeza para você, que é extremamente caro, quando você pode apenas chamar EnsureCapacity () em um ArrayList em vez ...
Philip Devine
5
@ Andreas: A LinkedList sempre aloca cinco vezes a memória do que uma matriz simples de referências; portanto, um requerimento ArrayListtemporário de 2,5 vezes ainda consome muito menos memória, mesmo enquanto a memória não é recuperada. Desde alocação grande variedade ignora o espaço Éden, eles não têm qualquer impacto sobre o comportamento GC, a menos que realmente não há memória suficiente, caso em que, a LinkedListexplodiu muito mais cedo ...
Holger
5
@ Andréas A outra questão é como a memória é alocada. LinkedListprecisa apenas de um pequeno pedaço de memória livre para alocar o próximo elemento. ArrayListprecisará de um bloco de espaço livre grande e contínuo para alocar a matriz redimensionada. Se o heap for fragmentado, o GC poderá acabar reordenando o heap inteiro apenas para liberar um único bloco de memória adequado.
Piotr Kusmierczyk 01/11/19
128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

Algoritmos: Notação Big-Oh

As ArrayLists são boas para escrever ou ler muitos ou anexos, mas são ruins para adicionar / remover da frente ou do meio.

Michael Munsey
fonte
42
Você não pode comparar valores grandes-O diretamente sem pensar em fatores constantes. Para listas pequenas (e a maioria das listas são pequenas), o O (N) do ArrayList é mais rápido que o O (1) do LinkedList.
Porculus 10/09/10
4
Não me importo com o desempenho de pequenas listas e o meu computador também não , a menos que seja usado em um loop de alguma forma.
Maarten Bodewes
45
LinkedList não pode realmente inserir no meio O(1). Ele precisa percorrer metade da lista para encontrar o ponto de inserção.
Thomas Ahle
8
LinkedList: insira no meio O (1) - está errado! Descobri que mesmo a inserção na posição 1/10 do tamanho do LinkedList é mais lenta do que a inserção de um elemento na posição 1/10 de um ArrayList. E ainda pior: o fim da coleção. inserindo últimas posições (não a última) de ArrayList é mais rápido, em seguida, em últimas posições (não a última) de LinkedList
Kachanov
14
@kachanov Inserir a LinkedList é O(1) se você tiver um iterador na posição de inserção , ou seja, ListIterator.addé supostamente O(1)para a LinkedList.
Quit - Anony-Mousse
107

Sim, eu sei, essa é uma pergunta antiga, mas vou colocar meus dois centavos:

O LinkedList é quase sempre a escolha errada, em termos de desempenho. Existem alguns algoritmos muito específicos nos quais uma LinkedList é solicitada, mas eles são muito, muito raros, e o algoritmo geralmente depende especificamente da capacidade do LinkedList de inserir e excluir elementos no meio da lista de forma relativamente rápida, depois de navegar por lá. com um ListIterator.

Há um caso de uso comum no qual o LinkedList supera o ArrayList: o de uma fila. No entanto, se seu objetivo é desempenho, em vez de LinkedList, considere também usar um ArrayBlockingQueue (se você pode determinar um limite superior do tamanho da sua fila com antecedência e pode alocar toda a memória antecipadamente) ou esta implementação CircularArrayList . (Sim, é de 2001, então você precisará gerá-lo, mas eu tenho índices de desempenho comparáveis ​​aos citados no artigo agora em uma JVM recente)

Daniel Martin
fonte
39
Do Java 6 você pode usar ArrayDeque. docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
Thomas Ahle
1
ArrayDequeé mais lento que a LinkedListmenos que todas as operações estejam no mesmo fim. Tudo bem quando usado como uma pilha, mas não cria uma boa fila.
Lista Jeremy
2
Falso - pelo menos para a implementação da Oracle em jdk1.7.0_60 e no teste a seguir. Eu criei um teste no qual repetir 10 milhões de vezes e tenho um Deque de 10 milhões de números inteiros aleatórios. Dentro do loop, examino um elemento e ofereço um elemento constante. No meu computador, o LinkedList é 10 vezes mais lento que o ArrayDeque e usa menos memória). O motivo é que, diferentemente de ArrayList, ArrayDeque mantém um ponteiro para a cabeça da matriz, para que não precise mover todos os elementos quando a cabeça é removida.
Henno Vermeulen
6
ArrayDequeprovavelmente será mais rápido do que Stackquando usado como uma pilha e mais rápido do que LinkedListquando usado como uma fila.
akhil_mittal
3
Observe que o comentário de akhil_mittal é uma citação da ArrayDequedocumentação.
Stuart Marks
65

É uma questão de eficiência. LinkedListé rápido para adicionar e excluir elementos, mas lento para acessar um elemento específico. ArrayListé rápido para acessar um elemento específico, mas pode ser lento para adicionar em qualquer extremidade e especialmente lento para excluir no meio.

Matriz vs ArrayList vs LinkedList vs Vector vai mais fundo, assim como a Lista vinculada .

dgtized
fonte
54

Correto ou incorreto: Por favor, execute o teste localmente e decida por si mesmo!

Editar / Remover é mais rápido LinkedListque ArrayList.

ArrayList, suportado por Array, que precisa ter o dobro do tamanho, é pior em aplicativos de grande volume.

Abaixo está o resultado do teste de unidade para cada operação. O tempo é dado em nanossegundos.


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

Aqui está o código:

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);
    }
}
Cinza
fonte
1
ArrayList não precisa ser dobrado, para ser preciso. Por favor, verifique as fontes primeiro.
Danubian Sailor
Deve-se observar que seu exemplo é defeituoso ... Você está removendo da cadeia de caracteres entre: 18 + [2, 12] bytes ("true0false", "true500000false"), em média 25 bytes, que são os tamanhos dos elementos no meio. Sabe-se que, à medida que o tamanho do byte do elemento aumenta, a lista vinculada apresenta um desempenho melhor, à medida que o tamanho da lista aumenta, uma matriz (lista) contígua se sai melhor. Mais importante, você está executando .equals () em strings - o que não é uma operação barata. Se você usou números inteiros, acho que haveria uma diferença.
Centril 21/08/14
2
"... é pior em aplicativos de grande volume ": este é um mal-entendido. LinkedListtem muito mais sobrecarga de memória porque para cada elemento existe um objeto de nó com cinco campos. Em muitos sistemas, isso gera 20 bytes de sobrecarga. A sobrecarga média de memória por elemento ArrayListé de uma palavra e meia, que gera 6 bytes e 8 bytes no pior caso.
Lii
1
Fiz uma versão melhor do seu benchmark aqui, com resultados - o desempenho do apêndice na extremidade para a lista de matrizes é artificialmente baixo para o seu, porque addAll está fornecendo uma matriz de armazenamento com tamanho EXATAMENTE inicial, portanto a primeira inserção sempre aciona um arraycopy. Além disso, isso inclui ciclos de aquecimento para permitir a compilação JIT antes da coleta de dados.
BobMcGee
4
@BillK desde o Java 8, você pode usar removeIf(element -> condition)onde ele se encaixa, o que pode ser significativamente mais rápido para um ArrayList, em comparação com o loop e a remoção via iterador, pois não é necessário mudar o restante inteiro para cada elemento individual. Se o desempenho é melhor ou pior do que LinkedListdepende do cenário específico, como LinkedListé O (1) em teoria, mas remover apenas um nó requer vários acessos à memória, que podem facilmente exceder o número necessário para a ArrayListremoção de um número significativo de elementos .
Holger
50

ArrayListé essencialmente uma matriz. LinkedListé implementado como uma lista vinculada dupla.

O geté bem claro. O (1) para ArrayList, porque ArrayListpermite acesso aleatório usando o índice. O (n) para LinkedList, porque ele precisa encontrar o índice primeiro. Nota: existem versões diferentes de adde remove.

LinkedListé mais rápido em adicionar e remover, mas mais lento em obter. Em resumo, LinkedListdeve ser preferido se:

  1. não há um grande número de acesso aleatório do elemento
  2. há um grande número de operações de adição / remoção

=== ArrayList ===

  • adicionar (E e)
    • adicionar no final de ArrayList
    • requer custo de redimensionamento de memória.
    • O (n) pior, O (1) amortizado
  • add (índice int, elemento E)
    • adicionar a uma posição de índice específica
    • requer mudança e possível custo de redimensionamento de memória
    • Em)
  • remove (int index)
    • remover um elemento especificado
    • requer mudança e possível custo de redimensionamento de memória
    • Em)
  • remove (Objeto o)
    • remova a primeira ocorrência do elemento especificado desta lista
    • precisa procurar o elemento primeiro e depois mudar e possível custo de redimensionamento de memória
    • Em)

=== LinkedList ===

  • adicionar (E e)

    • adicionar ao final da lista
    • O (1)
  • add (índice int, elemento E)

    • insira na posição especificada
    • precisa encontrar a posição primeiro
    • Em)
  • retirar()
    • remova o primeiro elemento da lista
    • O (1)
  • remove (int index)
    • remover elemento com índice especificado
    • precisa encontrar o elemento primeiro
    • Em)
  • remove (Objeto o)
    • remova a primeira ocorrência do elemento especificado
    • precisa encontrar o elemento primeiro
    • Em)

Aqui está uma figura de programcreek.com ( adde removeé o primeiro tipo, ou seja, adicione um elemento no final da lista e remova o elemento na posição especificada na lista.):

insira a descrição da imagem aqui

Ryan
fonte
3
"O LinkedList é mais rápido que adicionar / remover". Errado, verifique a resposta acima stackoverflow.com/a/7507740/638670
Nerrve
49

Joshua Bloch, autor do LinkedList:

Alguém realmente usa o LinkedList? Eu escrevi e nunca uso.

Link: https://twitter.com/joshbloch/status/583813919019573248

Sinto muito pela resposta por não ser tão informativa quanto as outras respostas, mas achei que seria a mais interessante e auto-explicativa.

Ruslan
fonte
34

ArrayListé acessível aleatoriamente, enquanto LinkedListé muito barato expandir e remover elementos. Para a maioria dos casos, tudo ArrayListbem.

A menos que você tenha criado grandes listas e medido um gargalo, provavelmente nunca precisará se preocupar com a diferença.

Dustin
fonte
15
LinkedList não é barato para adicionar elementos. É quase sempre mais rápido adicionar um milhão de elementos a um ArrayList do que adicioná-los a um LinkedList. E a maioria das listas no código do mundo real não tem nem um milhão de elementos.
Porculus 10/09/10
10
A qualquer momento, você sabe o custo de adicionar um item à sua LinkedList. O ArrayList você não (em geral). A adição de um único item a um ArrayList contendo um milhão de itens pode levar muito tempo - é uma operação O (n) mais o dobro do armazenamento, a menos que você pré-aloque espaço. A adição de um item a uma LinkedList é O (1). Minha última afirmação permanece.
Dustin
4
A adição de um único item a um ArrayList é O (1), não importa que seja 1 milhão ou 1 bilhão. A adição de um item a uma LinkedList também é O (1). "Adicionar" significa ADICIONAR AO FIM.
Kachanov 13/05
Você deve ter lido a implementação de maneira diferente da minha. Na minha experiência, copiar uma matriz de 1 bilhão de elementos leva mais tempo do que copiar uma matriz de 1 milhão de elementos.
Dustin
6
@kachanov você deve entender mal o Dustin. A menos que você tenha declarado uma matriz de 1 bilhão de itens, será necessário redimensionar sua matriz; nesse caso, você precisará copiar todos os elementos em uma nova matriz maior; portanto, algumas vezes você obterá O (N), mas com uma lista vinculada, você sempre get O (1)
Stan R.
29

O TL; DR, devido à arquitetura moderna do computador, ArrayListserá significativamente mais eficiente para praticamente qualquer caso de uso possível - e, portanto, LinkedListdeve ser evitado, exceto em casos extremos e muito únicos.


Em teoria, o LinkedList possui um O (1) para o add(E element)

A adição de um elemento no meio de uma lista também deve ser muito eficiente.

A prática é muito diferente, pois o LinkedList é uma estrutura de dados hostis de cache . Do ponto de vista do desempenho - há muito poucos casos em LinkedListque o desempenho pode ser melhor do que os compatíveis com o cacheArrayList .

Aqui estão os resultados de um teste de referência, inserindo elementos em locais aleatórios. Como você pode ver - a lista de matrizes é muito mais eficiente, embora em teoria cada inserção no meio da lista exija "mover" os n elementos posteriores da matriz (valores mais baixos são melhores):

insira a descrição da imagem aqui

Trabalhando em um hardware de última geração (caches maiores e mais eficientes) - os resultados são ainda mais conclusivos:

insira a descrição da imagem aqui

O LinkedList leva muito mais tempo para realizar o mesmo trabalho. código fonte

Há duas razões principais para isso:

  1. Principalmente - que os nós doLinkedList estão espalhados aleatoriamente pela memória. A RAM ("Memória de acesso aleatório") não é realmente aleatória e os blocos de memória precisam ser buscados no cache. Essa operação leva tempo e, quando tais buscas ocorrem com freqüência - as páginas de memória no cache precisam ser substituídas o tempo todo -> Falta de cache -> O cache não é eficiente. ArrayListos elementos são armazenados na memória contínua - e é exatamente para isso que a arquitetura moderna da CPU está otimizando.

  2. Secundário LinkedList necessário para manter os ponteiros de retrocesso / avanço, o que significa três vezes o consumo de memória por valor armazenado em comparação com ArrayList.

DynamicIntArray , btw, é uma retenção de implementação ArrayList personalizada Int(tipo primitivo) e não Objetos - portanto, todos os dados são realmente armazenados adjacentemente - portanto, ainda mais eficientes.

Um elemento-chave a ser lembrado é que o custo de buscar o bloco de memória é mais significativo do que o custo de acessar uma única célula de memória. É por isso que o leitor de 1 MB de memória seqüencial é até 400 vezes mais rápido do que ler essa quantidade de dados de diferentes blocos de memória:

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

Fonte: números de latência que todo programador deve saber

Apenas para tornar o argumento ainda mais claro, verifique a referência de adição de elementos ao início da lista. Este é um caso de uso em que, teoricamente, o LinkedListitem deve realmente brilhar e ArrayListdeve apresentar resultados ruins ou até piores:

insira a descrição da imagem aqui

Nota: esta é uma referência da biblioteca C ++ Std, mas minha experiência anterior mostrou que os resultados em C ++ e Java são muito semelhantes. Código fonte

Copiar um volume sequencial de memória é uma operação otimizada pelas CPUs modernas - mudando a teoria e tornando, de novo, ArrayList/ Vectormuito mais eficiente


Créditos: todos os benchmarks publicados aqui são criados por Kjell Hedström . Ainda mais dados podem ser encontrados em seu blog

Lior Bar-On
fonte
Eu não chamaria uma fila de única ou extrema! Uma fila fifo é muito mais fácil implementada em um LinkedList em vez de em um ArrayList. Na verdade, é um pesadelo em uma ArrayList, pois você precisa rastrear seu próprio início, parar e realizar sua própria realocação; você também pode usar uma matriz, mas uma lista vinculada é um quino. Não tenho certeza sobre a implementação do Java, mas um LinkedList pode fazer O (1) para operações de fila e desenfileiramento (requer um ponteiro especial para o elemento tail para a remoção, o que presumo que o java tenha, mas não verifiquei duas vezes .)
Bill K
24

Se o seu código possui add(0)e remove(0), use LinkedListae é mais bonito addFirst()e removeFirst()métodos. Caso contrário, use ArrayList.

E, é claro, o ImmutableList da Guava é seu melhor amigo.

Jesse Wilson
fonte
3
Para listas pequenas, o ArrayList.add (0) ainda será sempre mais rápido que o LinkedList.addFirst ().
Porculus 10/09/10
1
@ Porculus Estou constantemente ouvindo esse argumento de que, para pequenas listas, ArrayList.add (0) será mais rápido, este pequeno é o quanto pequeno? 10 elementos, 10 milhões,?
precisa saber é o seguinte
1
@ garg10may pequena é inferior a 10.
Jesse Wilson
@Porculus small significa menos que a capacidade máxima da matriz interna subjacente ao ArrayList.
Janac Meena
21

Sei que este é um post antigo, mas eu honestamente não posso acreditar que ninguém mencionou que os LinkedListimplementos Deque. Basta olhar para os métodos em Deque(e Queue); se você quiser uma comparação justa, tente executar LinkedListcontra ArrayDequee fazer uma comparação de recursos-de-recurso.

Ajax
fonte
18

Aqui está a notação Big-O em ambos ArrayListe LinkedListe também 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)

Com base nisso, você deve decidir o que escolher. :)

Rajith Delantha
fonte
9
>>>> ArrayList add -> O (1) <- não tru. Em alguns casos ArrayList terá que crescer para adicionar mais um elemento
Kachanov
1
ListaLigada de remoção não é O (1), seria necessário procurar o elemento a ser removido, por conseguinte, pior caso o (n) e S média (n / 2)
garg10may
Nem é LinkedList.add(), embora a maioria das respostas aqui diga isso.
Marquês de Lorne
18

Vamos comparar o LinkedList e o ArrayList abaixo dos parâmetros:

1. Implementação

ArrayList é a implementação redimensionável da matriz da interface da lista, enquanto

LinkedList é a implementação de lista vinculada duplamente da interface da lista.


2. Desempenho

  • operação get (int index) ou de pesquisa

    A operação ArrayList get (int index) é executada em tempo constante, ou seja, O (1) enquanto

    O tempo de execução da operação getLink (int index) é O (n).

    A razão por trás do ArrayList ser mais rápido que o LinkedList é que o ArrayList usa um sistema baseado em índice para seus elementos, pois internamente usa uma estrutura de dados da matriz, por outro lado,

    O LinkedList não fornece acesso baseado em índice para seus elementos, pois itera do começo ou do fim (o que estiver mais próximo) para recuperar o nó no índice de elemento especificado.

  • operação insert () ou add (Object)

    As inserções no LinkedList geralmente são rápidas em comparação com ArrayList. No LinkedList, a adição ou inserção é a operação O (1).

    Enquanto no ArrayList , se o array estiver completo, ou seja, no pior caso, haverá um custo extra de redimensionar o array e copiar elementos para o novo array, o que torna o tempo de execução da operação de adição no ArrayList O (n), caso contrário, é O (1) .

  • operação remover (int)

    A operação de remoção no LinkedList geralmente é igual à ArrayList, ou seja, O (n).

    No LinkedList , existem dois métodos de remoção sobrecarregados. um é remove () sem nenhum parâmetro que remova o cabeçalho da lista e seja executado em tempo constante O (1). O outro método de remoção sobrecarregado no LinkedList é remove (int) ou remove (Object) que remove o Object ou int passado como parâmetro. Esse método percorre o LinkedList até encontrar o objeto e desvinculá-lo da lista original. Portanto, esse tempo de execução do método é O (n).

    Enquanto no método remove (int) ArrayList envolve a cópia de elementos da matriz antiga para a nova matriz atualizada, portanto, seu tempo de execução é O (n).


3. Iterador Reverso

O LinkedList pode ser iterado na direção reversa usando descendingIterator () enquanto

não há descendingIterator () em ArrayList , portanto, precisamos escrever nosso próprio código para iterar sobre o ArrayList na direção reversa.


4. Capacidade inicial

Se o construtor não estiver sobrecarregado, ArrayList criará uma lista vazia da capacidade inicial 10, enquanto

LinkedList apenas constrói a lista vazia sem nenhuma capacidade inicial.


5. Sobrecarga de memória

A sobrecarga de memória no LinkedList é mais comparada ao ArrayList, pois um nó no LinkedList precisa manter os endereços do nó seguinte e anterior. Enquanto

Em ArrayList, cada índice mantém apenas o objeto real (dados).


Fonte

Abhijeet Ashok Muneshwar
fonte
18

Eu costumo usar um sobre o outro com base nas complexidades de tempo das operações que eu executaria nessa lista específica.

|---------------------|---------------------|--------------------|------------|
|      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    |
|--------------------------------------|
Gayan Weerakutti
fonte
O ArrayDeque equilibra as coisas um pouco mais em relação às matrizes, pois inserir / remover frente / trás são todas O (1), a única coisa que a Lista Vinculada ainda ganha é adicionar / remover enquanto estiver percorrendo (as operações do Iterator).
Bill K
14

Além dos outros bons argumentos acima, você deve observar a interface de ArrayListimplementos RandomAccess, enquanto LinkedListimplementa Queue.

Então, de alguma forma, eles abordam problemas um pouco diferentes, com diferenças de eficiência e comportamento (consulte a lista de métodos).

PhiLho
fonte
10

Depende de quais operações você fará mais na lista.

ArrayListé mais rápido acessar um valor indexado. É muito pior ao inserir ou excluir objetos.

Para saber mais, leia qualquer artigo que fale sobre a diferença entre matrizes e listas vinculadas.

Matthew Schinckel
fonte
2
Para saber mais, não leia, basta escrever o código. e você descobrirá que a implementação do ArrayList é mais rápida que o LinkedList na inserção e exclusão.
Kachanov 18/05
8

Uma lista de matrizes é essencialmente uma matriz com métodos para adicionar itens, etc. (e você deve usar uma lista genérica). É uma coleção de itens que podem ser acessados ​​através de um indexador (por exemplo [0]). Implica uma progressão de um item para o próximo.

Uma lista vinculada especifica uma progressão de um item para o próximo (Item a -> item b). Você pode obter o mesmo efeito com uma lista de matrizes, mas uma lista vinculada diz absolutamente que item deve seguir o anterior.

kemiller2002
fonte
7

Um recurso importante de uma lista vinculada (que eu não li em outra resposta) é a concatenação de duas listas. Com uma matriz, isso é O (n) (+ sobrecarga de algumas realocações) com uma lista vinculada, isso é apenas O (1) ou O (2) ;-)

Importante : Para Java, LinkedListisso não é verdade! Consulte Existe um método de concat rápido para lista vinculada em Java?

Karussell
fonte
2
Como é isso? Isso pode ser verdade com estruturas de dados de lista vinculada, mas não com um objeto Java LinkList. Você não pode simplesmente apontar a nextde uma lista para o primeiro nó na segunda lista. A única maneira é usar o addAll()que adiciona elementos sequencialmente, embora seja melhor do que percorrer e chamar add()cada elemento. Para fazer isso rapidamente em O (1), você precisaria de uma classe de composição (como org.apache.commons.collections.collection.CompositeCollection), mas isso funcionaria para qualquer tipo de lista / coleção.
Kevin Brock
sim, verdade. Eu editei a resposta em conformidade. mas veja esta resposta para 'como' fazê-lo com o LinkedList: stackoverflow.com/questions/2494031/…
Karussell
7

ArrayList e LinkedList têm seus próprios prós e contras.

ArrayList usa o endereço de memória contíguo em comparação com o LinkedList, que usa ponteiros para o próximo nó. Portanto, quando você deseja procurar um elemento em um ArrayList, é mais rápido que fazer iterações com o LinkedList.

Por outro lado, a inserção e exclusão em uma LinkedList são muito mais fáceis, porque você só precisa alterar os ponteiros, enquanto uma ArrayList implica o uso da operação shift para qualquer inserção ou exclusão.

Se você tiver operações de recuperação frequentes no seu aplicativo, use um ArrayList. Se você tiver inserção e exclusão freqüentes, use um LinkedList.

Nesan Mano
fonte
6

Li as respostas, mas há um cenário em que sempre uso uma LinkedList sobre uma ArrayList que quero compartilhar para ouvir opiniões:

Toda vez que eu tinha um método que retorna uma lista de dados obtidos de um banco de dados, eu sempre uso um LinkedList.

Minha lógica era que, porque é impossível saber exatamente quantos resultados estou obtendo, não haverá desperdício de memória (como em ArrayList com a diferença entre a capacidade e o número real de elementos) e não haveria desperdício de tempo tentando duplicar a capacidade.

Quanto a ArrayList, concordo que pelo menos você sempre deve usar o construtor com a capacidade inicial, para minimizar ao máximo a duplicação das matrizes.

gaijinco
fonte
5

ArrayListe LinkedListos implementos, List interface seus métodos e resultados são quase idênticos. No entanto, existem poucas diferenças entre eles que melhoram um ao outro, dependendo do requisito.

ArrayList Vs LinkedList

1) a Search: ArrayListoperação de pesquisa é bastante rápida em comparação com a LinkedListoperação de pesquisa. get(int index)in ArrayListfornece o desempenho de O(1)enquanto o LinkedListdesempenho é O(n).

Reason: ArrayListmantém um sistema baseado em índice para seus elementos, pois usa implicitamente a estrutura de dados da matriz, o que torna mais rápido a pesquisa de um elemento na lista. Por outro lado, LinkedListimplementa lista duplamente vinculada, que requer a passagem por todos os elementos para pesquisar um elemento.

2) a Deletion: LinkedListoperação de remoção fornece O(1)desempenho, enquanto ArrayListfornece desempenho variável: O(n)na pior das hipóteses (ao remover o primeiro elemento) e O(1)na melhor das hipóteses (ao remover o último elemento).

Conclusão: a exclusão do elemento LinkedList é mais rápida em comparação ao ArrayList.

Razão: Cada elemento do LinkedList mantém dois ponteiros (endereços) que apontam para os dois elementos vizinhos na lista. Portanto, a remoção requer apenas alteração na localização do ponteiro nos dois nós vizinhos (elementos) do nó que será removido. Enquanto Em ArrayList, todos os elementos precisam ser deslocados para preencher o espaço criado pelo elemento removido.

3) Inserts Performance: LinkedListmétodo add dá O(1)o desempenho enquanto ArrayListO(n)no pior caso. O motivo é o mesmo que o explicado para remover.

4) Memory Overhead: ArrayListmantém índices e dados de elementos enquanto LinkedListmantém dados de elementos e dois ponteiros para nós vizinhos

portanto, o consumo de memória é alto no LinkedList comparativamente.

Existem poucas semelhanças entre essas classes, que são as seguintes:

  • ArrayList e LinkedList são implementação da interface List.
  • Ambos mantêm a ordem de inserção dos elementos, o que significa que, ao exibir os elementos ArrayList e LinkedList, o conjunto de resultados teria a mesma ordem em que os elementos foram inseridos na lista.
  • Ambas as classes não são sincronizadas e podem ser sincronizadas explicitamente usando o método Collections.synchronizedList.
  • O iteratore listIteratorretornado por essas classes são fail-fast(se a lista for estruturalmente modificada a qualquer momento após a criação do iterador, de qualquer forma, exceto pelos iterator’spróprios métodos de remoção ou adição, o iterador throwa ConcurrentModificationException).

Quando usar o LinkedList e quando usar o ArrayList?

  • Conforme explicado acima, as operações de inserção e remoção oferecem bom desempenho (O(1))em LinkedListcomparação com ArrayList(O(n)).

    Portanto, se houver um requisito de adição e exclusão freqüentes no aplicativo, o LinkedList é a melhor opção.

  • As get methodoperações de pesquisa ( ) são rápidas, Arraylist (O(1))mas não emLinkedList (O(n))

    portanto, se houver menos operações de adição e remoção e mais requisitos de operações de pesquisa, ArrayList seria sua melhor aposta.

Real73
fonte
5

A operação get (i) no ArrayList é mais rápida que no LinkedList, porque:
ArrayList: implementação de matriz redimensionável da interface List
LinkedList: implementação de lista duplamente vinculada das interfaces List e Deque

As operações indexadas na lista percorrerão a lista desde o início ou o final, o que estiver mais próximo do índice especificado.

Amitābha
fonte
5

1) Estrutura de dados subjacente

A primeira diferença entre ArrayList e LinkedList vem com o fato de que ArrayList é apoiado por Array enquanto LinkedList é apoiado por LinkedList. Isso levará a outras diferenças no desempenho.

2) O LinkedList implementa o Deque

Outra diferença entre ArrayList e LinkedList é que, além da interface List, o LinkedList também implementa a interface Deque, que fornece as operações de primeiro a entrar para adicionar () e poll () e várias outras funções de Deque. 3) Adicionando elementos no ArrayList O elemento Add no ArrayList é a operação O (1) se não acionar o redimensionamento do Array; nesse caso, ele se tornará O (log (n)). Por outro lado, anexando um elemento no LinkedList é uma operação O (1), pois não requer nenhuma navegação.

4) Removendo um elemento de uma posição

Para remover um elemento de um índice específico, por exemplo, chamando remove (index), ArrayList executa uma operação de cópia que o aproxima de O (n), enquanto o LinkedList precisa passar para aquele ponto que também o torna O (n / 2) , pois ele pode percorrer de qualquer direção com base na proximidade.

5) Iterando sobre ArrayList ou LinkedList

A iteração é a operação O (n) para o LinkedList e o ArrayList em que n é o número de um elemento.

6) Recuperando elemento de uma posição

A operação get (index) é O (1) em ArrayList enquanto seu O (n / 2) em LinkedList, pois precisa percorrer até essa entrada. Embora, na notação O grande, O (n / 2) seja apenas O (n) porque ignoramos constantes lá.

7) Memória

O LinkedList usa um objeto wrapper, Entry, que é uma classe aninhada estática para armazenar dados e dois nós próximos e anteriores, enquanto o ArrayList apenas armazena dados no Array.

Portanto, o requisito de memória parece menos no caso de ArrayList do que no LinkedList, exceto no caso em que Array executa a operação de redimensionar tamanho quando copia o conteúdo de um Array para outro.

Se a matriz for grande o suficiente, poderá levar muita memória nesse momento e disparar a coleta de lixo, o que pode diminuir o tempo de resposta.

De todas as diferenças acima entre ArrayList e LinkedList, parece que ArrayList é a melhor escolha que o LinkedList em quase todos os casos, exceto quando você faz uma operação add () frequente do que remove () ou get ().

É mais fácil modificar uma lista vinculada do que ArrayList, especialmente se você estiver adicionando ou removendo elementos do início ou do fim, porque a lista vinculada mantém internamente referências dessas posições e elas são acessíveis em O (1).

Em outras palavras, você não precisa percorrer a lista vinculada para alcançar a posição em que deseja adicionar elementos; nesse caso, a adição se torna operação O (n). Por exemplo, inserindo ou excluindo um elemento no meio de uma lista vinculada.

Na minha opinião, use ArrayList sobre LinkedList para a maioria dos propósitos práticos em Java.

Anjali Suman
fonte
1
Penso que esta é a melhor resposta declarada de todo o grupo aqui. É preciso e informativo. Eu sugeriria alterar a última linha - no final, adicione "além das filas", que são estruturas muito importantes que realmente não fazem sentido para uma lista vinculada.
Bill K
3

Um dos testes que vi aqui apenas realiza o teste uma vez. Mas o que eu notei é que você precisa executar esses testes várias vezes e, eventualmente, o tempo deles irá convergir. Basicamente, a JVM precisa se aquecer. Para o meu caso de uso específico, eu precisava adicionar / remover itens a uma lista que aumenta para cerca de 500 itens. Nos meus testes, LinkedListsaiu mais rápido, LinkedListchegando a cerca de 50.000 NS e ArrayListchegando a cerca de 90.000 NS ... mais ou menos. Veja o código abaixo.

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

Remove () e insert () têm uma eficiência de tempo de execução de O (n) para ArrayLists e LinkedLists. No entanto, a razão por trás do tempo de processamento linear vem de duas razões muito diferentes:

Em um ArrayList, você chega ao elemento em O (1), mas na verdade a remoção ou a inserção de algo o torna O (n) porque todos os seguintes elementos precisam ser alterados.

Em um LinkedList, é necessário O (n) para realmente chegar ao elemento desejado, porque temos que começar desde o início até atingir o índice desejado. Na verdade, remover ou inserir é constante, porque precisamos alterar apenas 1 referência para remove () e 2 referências para insert ().

Qual dos dois é mais rápido para inserir e remover depende de onde isso acontece. Se estivermos mais próximos do início, o LinkedList será mais rápido, porque precisamos passar por relativamente poucos elementos. Se estivermos mais perto do final, um ArrayList será mais rápido, porque chegamos lá em tempo constante e só precisamos alterar os poucos elementos restantes que o seguem. Quando feito precisamente no meio, o LinkedList será mais rápido porque passar por n elementos é mais rápido que mover n valores.

Bônus: Embora não haja como criar esses dois métodos O (1) para um ArrayList, na verdade há um modo de fazer isso no LinkedLists. Digamos que queremos passar por toda a Lista removendo e inserindo elementos em nosso caminho. Normalmente, você começaria do início para cada elemento usando o LinkedList; também poderíamos "salvar" o elemento atual em que estamos trabalhando com um Iterador. Com a ajuda do Iterator, obtemos uma eficiência O (1) para remover () e insert () ao trabalhar em uma LinkedList. Tornando-o o único benefício de desempenho, eu sei onde um LinkedList é sempre melhor que um ArrayList.

pietz
fonte
1

ArrayList estende AbstractList e implementa a Interface da lista. ArrayList é uma matriz dinâmica.
Pode-se dizer que foi basicamente criado para superar as desvantagens de matrizes.

A classe LinkedList estende AbstractSequentialList e implementa a interface List, Deque e Queue.
O desempenho
arraylist.get()é O (1) enquanto linkedlist.get()O (n)
arraylist.add()é O (1) e linkedlist.add()é 0 (1)
arraylist.contains()é O (n) e linkedlist.contains()é O (n)
arraylist.next()é O (1) e linkedlist.next()é O (1)
arraylist.remove()é O (n) Considerando que linkedlist.remove()é O (1)
Na matriz
iterator.remove()é O (n)
Enquanto Na lista vinculada
iterator.remove()é O (1)

Randhawa
fonte