web-dev-qa-db-fra.com

Quand utiliser LinkedList sur ArrayList en Java?

J'ai toujours été quelqu'un à utiliser simplement:

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

J'utilise l'interface comme nom de type pour portability. Ainsi, lorsque je pose des questions de ce type, je peux retravailler mon code. 

Quand LinkedList doit-il être utilisé sur ArrayList et vice-versa?

2750
sdellysse

RésuméArrayList avec ArrayDeque sont préférables dans beaucoup plus de cas d'utilisation que LinkedList. Si vous n'êtes pas sûr, commencez par ArrayList.


LinkedList et ArrayList sont deux implémentations différentes de l'interface List. LinkedList l'implémente avec une liste doublement chaînée. ArrayList l'implémente avec un tableau de redimensionnement dynamique.

Comme pour les opérations de tableau lié et de tableau standard, les différentes méthodes auront des exécutions algorithmiques différentes.

Pour LinkedList<E>

  • get(int index) est O(n) (avec n/4 pas en moyenne)
  • add(E element) est O(1)
  • add(int index, E element) est O(n) (avec n/4 pas en moyenne), Mais O(1) quand index = 0 <--- principal avantage de LinkedList<E>
  • remove(int index) est O(n) (avec n/4 pas en moyenne)
  • Iterator.remove() est O(1). <--- principal avantage de LinkedList<E>
  • ListIterator.add(E element) est O(1) C'est l'un des principaux avantages de LinkedList<E>

Remarque: La plupart des opérations nécessitent en moyenne n/4 étapes, constant nombre d'étapes dans le meilleur des cas (par exemple, index = 0) et n/2 étapes dans le pire des cas ( milieu de liste)

Pour ArrayList<E>

  • get(int index) est O(1) <--- principal avantage de ArrayList<E>
  • add(E element) est O(1) amorti, mais O(n) dans le pire des cas puisque le tableau doit être redimensionné et copié
  • add(int index, E element) est O(n) (avec n/2 pas en moyenne)
  • remove(int index) est O(n) (avec n/2 pas en moyenne)
  • Iterator.remove() est O(n) (avec n/2 pas en moyenne)
  • ListIterator.add(E element) est O(n) (avec n/2 pas en moyenne)

Remarque: De nombreuses opérations nécessitent en moyenne n/2 étapes, constant nombre d’étapes dans le meilleur des cas (fin de liste), n étapes dans le pire des cas (début de la liste). )

LinkedList<E> permet des insertions ou des suppressions de temps constant en utilisant des itérateurs, mais uniquement un accès séquentiel aux éléments. En d'autres termes, vous pouvez parcourir la liste en avant ou en arrière, mais trouver une position dans la liste prend du temps proportionnellement à la taille de la liste. Javadoc dit "les opérations qui indexent dans la liste parcourent la liste depuis le début ou la fin, selon ce qui est le plus proche", ces méthodes sont donc O(n) (n/4 étapes) en moyenne, bien que O(1) pour index = 0.

ArrayList<E> permet en revanche un accès rapide en lecture aléatoire, ce qui vous permet de saisir n'importe quel élément en temps constant. Mais ajouter ou supprimer de n’importe où sauf la fin nécessite de déplacer tous ces derniers éléments, que ce soit pour faire une ouverture ou pour combler le vide. De même, si vous ajoutez plus d'éléments que la capacité du tableau sous-jacent, un nouveau tableau (1,5 fois la taille) est alloué et l'ancien tableau est copié dans le nouveau. Par conséquent, si vous ajoutez à un ArrayList, c'est O(n) dans le pire des cas mais constant en moyenne.

Ainsi, en fonction des opérations que vous avez l'intention de faire, vous devez choisir les implémentations en conséquence. Itérer sur l'un ou l'autre type de liste est pratiquement aussi bon marché. (Itérer sur un ArrayList est techniquement plus rapide, mais à moins que vous ne fassiez quelque chose de très sensible à la performance, vous ne devriez pas vous inquiéter à ce sujet - ce sont deux constantes.)

L’utilisation de LinkedList présente les principaux avantages lorsque vous réutilisez des itérateurs existants pour insérer et supprimer des éléments. Ces opérations peuvent ensuite être effectuées dans O(1) en modifiant la liste localement uniquement. Dans une liste de tableaux, le reste du tableau doit être déplacé (c.-à-d. Copié). De l’autre côté, rechercher dans une variable LinkedList signifie suivre les liens dans les étapes O(n) (n/2) pour le pire des cas, alors que dans une variable ArrayList, la position être calculé mathématiquement et utilisé dans O(1).

L’utilisation de LinkedList présente un autre avantage lorsque vous ajoutez ou supprimez de l’en-tête de la liste, puisque ces opérations sont O(1), alors qu’elles sont O(n) pour ArrayList. Notez que ArrayDeque peut être une bonne alternative à LinkedList pour l’ajout et le retrait de la tête, mais ce n’est pas un List.

De même, si vous avez de grandes listes, gardez à l'esprit que l'utilisation de la mémoire est également différente. Chaque élément d'un LinkedList a plus de temps système car les pointeurs sur les éléments suivant et précédent sont également stockés. ArrayLists n'a pas cette surcharge. Cependant, ArrayLists utilise la quantité de mémoire allouée pour la capacité, que des éléments aient été réellement ajoutés ou non.

La capacité initiale par défaut d'un ArrayList est assez petite (10 de Java 1.4 à 1.8). Mais comme l'implémentation sous-jacente est un tableau, vous devez le redimensionner si vous ajoutez beaucoup d'éléments. Pour éviter le coût élevé du redimensionnement lorsque vous savez que vous allez ajouter de nombreux éléments, construisez ArrayList avec une capacité initiale supérieure.

3039
Jonathan Tran

Jusqu’à présent, personne ne semble avoir abordé l’empreinte mémoire de chacune de ces listes, mis à part le consensus général selon lequel une LinkedList est "beaucoup plus" qu'un ArrayList et j’ai fait quelques calculs pour démontrer à quel point les deux listes prennent pour N références nulles.

Puisque les références sont 32 ou 64 bits (même lorsqu'elles sont nulles) sur leurs systèmes respectifs, j'ai inclus 4 jeux de données pour 32 et 64 bits LinkedLists et ArrayLists.

Remarque: Les tailles indiquées pour les lignes ArrayList correspondent à des listes réduites - En pratique, la capacité du sauvegarde tableau dans un ArrayList est généralement plus grand que son nombre d'éléments actuel.

Note 2: (merci BeeOnRope) Comme CompressedOops est la valeur par défaut à partir de la mi-JDK6 et plus, les valeurs ci-dessous pour 64 bits les machines correspondront à leurs contreparties 32 bits, à moins bien sûr que vous ne le désactiviez spécifiquement.


Graph of LinkedList and ArrayList No. of Elements x Bytes


Le résultat montre clairement que LinkedList est beaucoup plus que ArrayList, en particulier avec un nombre d'éléments très élevé. Si la mémoire est un facteur, éloignez-vous de LinkedLists.

Les formules que j'ai utilisées suivent, laissez-moi savoir si j'ai fait quelque chose de mal et je vais le réparer. "b" correspond à 4 ou 8 pour les systèmes 32 ou 64 bits, et "n" correspond au nombre d'éléments. Notez que la raison des mods est que tous les objets de Java occuperont un espace multiple de 8 octets, qu'ils soient tous utilisés ou non.

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)

598
Numeron

ArrayList est ce que vous voulez. LinkedList est presque toujours un bogue (de performance).

Pourquoi LinkedList est nul:

  • Il utilise beaucoup de petits objets de mémoire et a donc un impact sur les performances tout au long du processus.
  • Beaucoup de petits objets sont mauvais pour la cache-localité.
  • Toute opération indexée nécessite une traversée, c’est-à-dire une performance O(n). Cela n’est pas évident dans le code source, ce qui entraîne des algorithmes O(n) plus lents que si ArrayList était utilisé.
  • Obtenir de bonnes performances est délicat.
  • Même lorsque la performance big-O est identique à ArrayList, elle sera probablement beaucoup plus lente de toute façon.
  • Il est choquant de voir LinkedList dans le code source car c'est probablement le mauvais choix.
217

En tant que spécialiste de la performance opérationnelle à très grande échelle SOA Web depuis environ une décennie, je préférerais le comportement de LinkedList à celui de ArrayList. Alors que le débit de LinkedList en état stable est pire et peut donc conduire à acheter plus de matériel, le comportement de ArrayList sous pression peut conduire les applications d'un cluster à étendre leurs baies de manière presque synchronicité et, pour les grandes tailles, à un manque de réactivité dans l'application et une panne, alors que sous pression, ce qui est un comportement catastrophique.

De même, vous pouvez obtenir un meilleur débit dans une application grâce au collecteur de mémoire permanent par défaut, mais une fois que vous obtenez Java applications avec des piles de 10 Go, vous pouvez finir par verrouiller l’application pendant 25 secondes lors d’une délais d'attente et échecs dans SOA applications et bloque vos contrats de niveau de service si cela se produit trop souvent. Même si le collecteur CMS utilise plus de ressources et n'atteint pas le même débit brut, il s'agit d'un bien meilleur choix car il a une latence plus prévisible et plus réduite.

ArrayList n’est un meilleur choix en termes de performances que si vous voulez uniquement performance et que vous pouvez ignorer la latence. D'après mon expérience professionnelle, je ne peux pas ignorer la latence la plus défavorable.

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

Algorithmes: Big-Oh Notation

Les listes de tableaux sont bonnes pour l'écriture, les lectures uniques ou les ajouts, mais pas pour les ajouts/suppressions au début ou au milieu.

117
Michael Munsey

Oui, je sais, c'est une question ancienne, mais je vais ajouter mes deux sous:

LinkedList est presque toujours le mauvais choix en termes de performances. Il existe certains algorithmes très spécifiques pour lesquels une liste liée est demandée, mais ceux-ci sont très rares et l'algorithme dépendra généralement spécifiquement de la capacité de LinkedList d'insérer et de supprimer des éléments au milieu de la liste relativement rapidement, une fois que vous y aurez navigué. avec un ListIterator.

Il existe un cas d'utilisation courant dans lequel LinkedList surpasse ArrayList: celui d'une file d'attente. Toutefois, si votre objectif est la performance, au lieu de LinkedList, vous devriez également envisager d’utiliser un ArrayBlockingQueue (si vous pouvez déterminer une limite supérieure de la taille de votre file d’attente à l’avance et pouvoir vous permettre d’allouer toute la mémoire à l’avance), ou ceci Implémentation CircularArrayList . (Oui, c'est à partir de 2001, vous devrez donc le générer, mais j'ai des ratios de performance comparables à ceux cités dans l'article de la machine virtuelle récente)

97
Daniel Martin

C'est une question d'efficacité. LinkedList est rapide pour ajouter et supprimer des éléments, mais il est lent pour accéder à un élément spécifique. ArrayList est rapide pour accéder à un élément spécifique mais peut être lent à ajouter à l'une ou l'autre extrémité, et particulièrement lent à supprimer au milieu.

Array vs ArrayList vs LinkedList vs Vector va plus en profondeur, tout comme Linked List .

55
dgtized

Correct ou Incorrect: Veuillez exécuter le test localement et décider vous-même!

L'édition/suppression est plus rapide dans LinkedList que ArrayList.

ArrayList, soutenu par Array, qui doit être le double de sa taille, est pire dans les applications à grand volume.

Vous trouverez ci-dessous le résultat du test unitaire pour chaque opération. Le chronométrage est donné en nanosecondes.


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

Voici le code:

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

import Java.util.*;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        watch.start();

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

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

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

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

        watch.start();

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    class Watch {
        private long startTime;
        private long endTime;

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

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

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


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

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

ArrayList est essentiellement un tableau. LinkedList est implémenté sous forme de liste à double liaison. 

La get est assez claire. O(1) pour ArrayList, car ArrayList permet un accès aléatoire à l'aide de l'index. O(n) pour LinkedList, car il doit d'abord trouver l'index. Remarque: il existe différentes versions de add et remove

LinkedList est plus rapide à ajouter et à supprimer, mais plus lent à get. En résumé, LinkedList devrait être préféré si: 

  1. il n'y a pas de grand nombre d'accès aléatoire d'élément 
  2. il y a un grand nombre d'opérations d'ajout/suppression

=== ArrayList ===

  • ajouter (E e)
    • ajouter à la fin de ArrayList
    • nécessite des coûts de redimensionnement de la mémoire. 
    • O (n) pire, O(1) amorti
  • add (int index, élément E)
    • ajouter à une position d'index spécifique
    • nécessite un décalage et un éventuel coût de redimensionnement de la mémoire
    • O(n)
  • remove (int index)
    • supprimer un élément spécifié
    • nécessite un décalage et un éventuel coût de redimensionnement de la mémoire
    • O(n)
  • remove (Object o)
    • supprimer la première occurrence de l'élément spécifié de cette liste
    • besoin de rechercher l'élément en premier, puis de décaler et le coût de redimensionnement de la mémoire possible
    • O(n)

=== LinkedList ===

  • ajouter (E e)

    • ajouter à la fin de la liste
    • O(1)
  • add (int index, élément E)

    • insérer à la position spécifiée
    • besoin de trouver le poste en premier
    • O(n)
  • retirer()
    • supprimer le premier élément de la liste
    • O(1)
  • remove (int index)
    • supprimer l'élément avec l'index spécifié
    • besoin de trouver l'élément en premier
    • O(n)
  • remove (Object o)
    • supprimer la première occurrence de l'élément spécifié
    • besoin de trouver l'élément en premier
    • O(n)

Voici une figure de programcreek.com (add et remove sont le premier type, c’est-à-dire ajouter un élément à la fin de la liste et supprimer l’élément à la position spécifiée dans la liste.):

enter image description here

44
Ryan

1) Search: L'opération de recherche ArrayList est assez rapide comparée à l'opération de recherche LinkedList. get (int index) dans ArrayList donne les performances de O(1) alors que les performances de LinkedList sont de O (n).

Raison: ArrayList maintient un système basé sur un index pour ses éléments, car il utilise implicitement la structure de données de tableau, ce qui accélère la recherche d'un élément dans la liste. De l'autre côté, LinkedList implémente une liste doublement chaînée qui nécessite la traversée de tous les éléments pour rechercher un élément.

2) Suppression: L'opération de suppression de LinkedList donne O(1) des performances tandis que ArrayList donne des performances variables: O(n) dans le pire des cas (en supprimant le premier élément) et O(1) dans le meilleur des cas (en supprimant le dernier élément).

Conclusion: La suppression de l'élément LinkedList est plus rapide que celle de ArrayList.

Raison: Chaque élément de LinkedList conserve deux pointeurs (adresses) pointant vers les deux éléments voisins de la liste. Par conséquent, la suppression nécessite uniquement une modification de l'emplacement du pointeur dans les deux nœuds voisins (éléments) du nœud à supprimer. Dans ArrayList, tous les éléments doivent être déplacés pour remplir l'espace créé par l'élément supprimé.

3) Insère Performance: La méthode d'ajout LinkedList donne des performances O(1) alors que ArrayList donne O(n) dans le pire des cas. La raison est la même que celle expliquée pour supprimer.

4) Mémoire surcharge: ArrayList conserve les index et les données d'élément tandis que LinkedList conserve les données d'élément et deux pointeurs pour les nœuds voisins, d'où une consommation de mémoire élevée dans LinkedList.

Il y a peu de similitudes entre ces classes qui sont comme suit:

ArrayList et LinkedList sont tous deux des implémentations de l'interface List . Ils conservent tous les deux l'ordre d'insertion des éléments, ce qui signifie que lors de l'affichage des éléments ArrayList et LinkedList, le jeu de résultats aurait le même ordre d'insertion dans la liste . Ces deux classes ne sont pas synchronisées et peuvent être synchronisées explicitement à l'aide de la méthode Collections.synchronizedList . L'itérateur et le listIterator renvoyés par ces classes ont la capacité de résister rapidement (si la liste est structurellement modifiée à tout moment après la création de l'itérateur) , de quelque manière que ce soit, sauf par le biais des propres méthodes de suppression ou d’ajout de l’itérateur, celui-ci lève une exception ConcurrentModificationException).

Quand utiliser LinkedList et quand utiliser ArrayList?

1) Comme expliqué ci-dessus, les opérations d'insertion et d'extraction donnent de bonnes performances (O (1)) dans LinkedList par rapport à ArrayList (O (n)). Par conséquent, s'il est nécessaire d'ajouter et de supprimer fréquemment une application, alors LinkedList est le meilleur choix.

2) Les opérations de recherche (méthode) sont rapides dans ArrayList (O (1)) mais pas dans LinkedList (O (n)). Par conséquent, s'il y a moins d'opérations d'ajout et de suppression et davantage d'exigences en matière d'opérations de recherche, ArrayList serait votre meilleur pari.

35
NoNaMe

Joshua Bloch, l'auteur de LinkedList:

Est-ce que quelqu'un utilise réellement LinkedList? Je l'ai écrit et je ne l'utilise jamais.

Lien: https://Twitter.com/joshbloch/status/583813919019573248

Je suis désolé pour la réponse qui n’a pas été aussi informative que les autres réponses, mais j’ai pensé que ce serait la solution la plus intéressante et la plus explicite.

33
Ruslan

ArrayList est accessible de manière aléatoire, alors que LinkedList est vraiment peu coûteux pour développer et supprimer des éléments. Dans la plupart des cas, ArrayList convient.

Sauf si vous avez créé de grandes listes et mesuré un goulet d'étranglement, vous n'aurez probablement jamais à vous soucier de la différence.

32
Dustin

Si votre code a add(0) et remove(0), utilisez un LinkedList et ses méthodes plus jolies addFirst() et removeFirst(). Sinon, utilisez ArrayList.

Et bien sûr, Guava 's ImmutableList est votre meilleur ami.

20
Jesse Wilson

Je sais que ceci est un ancien post, mais honnêtement, je ne peux pas croire que personne n'ait mentionné que LinkedList implémente Deque. Il suffit de regarder les méthodes dans Deque (et Queue); si vous voulez une comparaison équitable, essayez d'exécuter LinkedList contre ArrayDeque et effectuez une comparaison fonctionnalité par fonctionnalité. 

20
Ajax

Voici la notation Big-O dans ArrayList et LinkedList et aussi 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)

Sur cette base, vous devez décider quoi choisir. :)

18
Rajith Delantha

Comparons LinkedList et ArrayList w.r.t. paramètres ci-dessous:

1. mise en œuvre

ArrayList est l'implémentation d'un tableau redimensionnable de l'interface de liste, alors que 

LinkedList est l'implémentation en liste à double liaison de l'interface de liste.


2. la performance

  • get (int index) ou opération de recherche

    L'opération ArrayList get (int index) s'exécute en temps constant, c'est-à-dire O(1) tant que 

    LinkedList L'exécution de l'opération get (int index) est O(n).

    La raison pour laquelle ArrayList étant plus rapide que LinkedList est que ArrayList utilise un système basé sur un index pour ses éléments, étant donné qu’il utilise en interne une structure de données de tableau,

    LinkedList ne fournit pas d'accès basé sur l'index pour ses éléments, car il effectue une itération du début ou de la fin (celle qui est la plus proche) pour extraire le nœud à l'index spécifié.

  • opération insert () ou add (Object)

    Les insertions dans LinkedList sont généralement rapides comparées à ArrayList. Dans LinkedList, l’ajout ou l’insertion est une opération O(1). 

    Tandis que dans ArrayList, si le tableau est plein, c'est-à-dire que le pire des cas, il y a un coût supplémentaire de redimensionnement et de copie des éléments dans le nouveau O (1).

  • enlever (int) opération

    Supprimer l'opération dans LinkedList est généralement identique à ArrayList, c'est-à-dire O (n).

    Dans LinkedList, il existe deux méthodes de suppression surchargées. on est remove () sans aucun paramètre qui supprime la tête de la liste et s'exécute à temps constant O (1). L'autre méthode remove surchargée dans LinkedList est remove (int) ou remove (Object), qui supprime l'objet ou l'int transmis en tant que paramètre. Cette méthode parcourt la liste LinkedList jusqu'à ce qu'elle trouve l'objet et la dissocie de la liste d'origine. Par conséquent, le temps d'exécution de cette méthode est O (n). 

    Dans la méthode ArrayList remove (int) implique la copie des éléments de l'ancien tableau vers le nouveau tableau mis à jour, son exécution est donc O (n).


3. Itérateur inversé

LinkedList peut être itéré dans le sens inverse en utilisant descendingIterator () tant que 

il n'y a pas de descendingIterator () dans ArrayList, nous devons donc écrire notre propre code pour itérer sur ArrayList en sens inverse.


4. Capacité initiale

Si le constructeur n'est pas surchargé, alors ArrayList crée une liste vide de capacité initiale 10, tandis que 

LinkedList ne construit que la liste vide sans aucune capacité initiale.


5. Mémoire Overhead

La surcharge de mémoire dans LinkedList est plus importante que ArrayList puisqu'un nœud dans LinkedList doit conserver les adresses du nœud suivant et du nœud précédent. Tandis que 

Dans ArrayList, chaque index ne contient que l’objet réel (données).


La source

14

En plus des autres arguments valables ci-dessus, vous devriez noter que ArrayList implémente l'interface RandomAccess, tandis que LinkedList implémente Queue.

Ils abordent donc des problèmes légèrement différents, avec des différences d'efficacité et de comportement (voir la liste de leurs méthodes).

12
PhiLho
9
chharvey

Une liste de tableaux est essentiellement un tableau avec des méthodes pour ajouter des éléments, etc. (et vous devriez plutôt utiliser une liste générique). Il s'agit d'un ensemble d'éléments accessibles via un indexeur (par exemple [0]). Cela implique une progression d'un élément à l'autre.

Une liste chaînée indique une progression d'un élément à l'autre (élément a -> élément b). Vous pouvez obtenir le même effet avec une liste de tableaux, mais une liste chaînée indique absolument quel élément est censé suivre le précédent. 

8
kemiller2002

Cela dépend des opérations que vous ferez plus sur la liste.

ArrayList est plus rapide pour accéder à une valeur indexée. C’est bien pire lors de l’insertion ou de la suppression d’objets.

Pour en savoir plus, lisez tout article traitant de la différence entre les tableaux et les listes chaînées.

7
Matthew Schinckel

J'ai lu les réponses, mais il existe un scénario dans lequel j'utilise toujours une liste LinkedList plutôt qu'une liste ArrayList que je souhaite partager pour entendre les opinions:

Chaque fois que j'ai eu une méthode qui retourne une liste de données obtenue à partir d'une base de données, j'utilise toujours une LinkedList.

Mon raisonnement était qu’étant donné qu’il était impossible de savoir exactement combien de résultats puis-je obtenir, la mémoire ne serait pas perdue (comme dans ArrayList avec la différence entre la capacité et le nombre réel d’éléments), et il n’y aurait aucune perte de temps à essayer de dupliquer la capacité.

En ce qui concerne ArrayList, je suis d’accord qu’au moins, vous devriez toujours utiliser le constructeur avec la capacité initiale, afin de minimiser autant que possible la duplication des tableaux.

6
gaijinco

Une caractéristique importante d'une liste chaînée (que je n'ai pas lue dans une autre réponse) est la concaténation de deux listes. Avec un tableau, il s'agit de O(n) (+ surcharge de certaines réaffectations); avec une liste chaînée, il ne s'agit que de O(1) ou de O(2) ;-)

Important: Pour Java sa LinkedList, ce n'est pas vrai! Voir Existe-t-il une méthode de concat rapide pour la liste chaînée en Java?

6
Karussell

J'utilise habituellement l'un sur l'autre en fonction de la complexité temporelle des opérations que j'effectuerais sur cette liste particulière. 

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


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

ArrayList et LinkedList implémentent List interface et leurs méthodes et résultats sont presque identiques. Cependant, il y a peu de différences entre eux qui rendent un meilleur sur un autre selon les besoins.

ArrayList Vs LinkedList

1) L'opération de recherche Search:ArrayList est assez rapide comparée à l'opération de recherche LinkedList. get(int index) dans ArrayList donne la performance de O(1) tandis que LinkedList est O(n).

Reason:ArrayList maintient le système basé sur l'index pour ses éléments, car il utilise implicitement la structure de données de tableau, ce qui accélère la recherche d'un élément dans la liste. De l'autre côté, LinkedList implémente une liste doublement chaînée qui nécessite la traversée de tous les éléments pour rechercher un élément.

2) Deletion:LinkedList remove opération donne la performance O(1) tandis que ArrayList donne la performance variable: O(n) dans le pire des cas (lors de la suppression du premier élément) et O(1) dans le meilleur des cas (lors de la suppression du dernier élément).

Conclusion: la suppression d'éléments LinkedList est plus rapide que celle de ArrayList.

Raison: chaque élément de LinkedList conserve deux pointeurs (adresses) qui pointent vers les deux éléments voisins de la liste. Par conséquent, la suppression nécessite uniquement de modifier l'emplacement du pointeur dans les deux nœuds voisins (éléments) du nœud à supprimer. Dans ArrayList, tous les éléments doivent être déplacés pour remplir l'espace créé par l'élément supprimé.

3) Inserts Performance:LinkedList add La méthode add donne les performances de O(1) tandis que ArrayList donne O(n) dans le pire des cas. La raison est la même que celle expliquée pour supprimer.

4) Memory Overhead:ArrayList conserve les index et les données d'élément tandis que LinkedList conserve les données d'élément et deux pointeurs pour les nœuds voisins 

par conséquent, la consommation de mémoire est relativement élevée dans LinkedList.

Il y a peu de similitudes entre ces classes qui sont comme suit:

  • ArrayList et LinkedList sont tous deux des implémentations de l'interface List.
  • Ils conservent tous les deux l'ordre d'insertion des éléments, ce qui signifie que, lors de l'affichage des éléments ArrayList et LinkedList, le jeu de résultats aurait le même ordre d'insertion dans la liste.
  • Ces deux classes ne sont pas synchronisées et peuvent être synchronisées explicitement à l'aide de la méthode Collections.synchronizedList.
  • Les iterator et listIterator renvoyés par ces classes sont fail-fast (si la liste est structurellement modifiée à tout moment après la création de l'itérateur, de quelque manière que ce soit, sauf via les méthodes iterator’s own remove ou add, l'itérateur sera throw a ConcurrentModificationException).

Quand utiliser LinkedList et quand utiliser ArrayList?

  • Comme expliqué ci-dessus, les opérations d'insertion et de suppression permettent d'obtenir de bonnes performances (O(1)) dans LinkedList par rapport à ArrayList(O(n)).

    Par conséquent, s’il existe une exigence d’ajouts et de suppressions fréquents dans une application, alors LinkedList est le meilleur choix.

  • Les opérations de recherche (get method) sont rapides dans Arraylist (O(1)) mais pas dans LinkedList (O(n))

    donc, s'il y a moins d'opérations d'ajout et de suppression et plus d'exigences en matière d'opérations de recherche, ArrayList serait votre meilleur choix.

5
Real73

L'opération get (i) dans ArrayList est plus rapide que LinkedList, car:
ArrayList: Implémentation d'un tableau redimensionnable de l'interface List
LinkedList: Mise en oeuvre de la liste à double liaison des interfaces List et Deque

Les opérations qui indexent dans la liste parcourent la liste depuis le début ou la fin, selon ce qui est le plus proche de l'index spécifié. 

5
Amitābha

ArrayList et LinkedList ont leurs propres avantages et inconvénients.

ArrayList utilise une adresse mémoire contiguë par rapport à LinkedList qui utilise des pointeurs vers le nœud suivant. Ainsi, lorsque vous souhaitez rechercher un élément dans une liste de tableaux, cela est plus rapide que d'effectuer n itérations avec LinkedList.

D'autre part, l'insertion et la suppression dans une liste liée sont beaucoup plus faciles car il vous suffit de changer les pointeurs, alors qu'une liste de tableaux implique l'utilisation de l'opération shift pour toute insertion ou suppression.

Si vous utilisez fréquemment des opérations de récupération dans votre application, utilisez un ArrayList. Si vous avez des insertions et des suppressions fréquentes, utilisez une LinkedList. 

5
Nesan Mano

Remove () et insert () ont toutes deux une efficacité d'exécution de O(n) pour ArrayLists et LinkedLists. Cependant, la raison du temps de traitement linéaire provient de deux raisons très différentes:

Dans une ArrayList, vous accédez à l'élément dans O (1), mais supprimer ou insérer quelque chose le rend O(n) car tous les éléments suivants doivent être modifiés.

Dans une LinkedList, il faut O(n) pour atteindre l'élément souhaité, car nous devons commencer au tout début jusqu'à atteindre l'index souhaité. En fait, supprimer ou insérer est constant car il suffit de changer 1 référence pour remove () et 2 références pour insert ().

Lequel des deux est plus rapide pour l'insertion et le retrait dépend de l'endroit où cela se produit. Si nous sommes plus près du début, la LinkedList sera plus rapide, car nous devons passer en revue relativement peu d'éléments. Si nous sommes proches de la fin, un ArrayList sera plus rapide, car nous y arriverons en un temps constant et nous n’aurons plus qu’à changer les quelques éléments qui le suivent. Lorsque cela est fait précisément au milieu, la LinkedList sera plus rapide, car parcourir n éléments est plus rapide que déplacer n valeurs.

Bonus: Bien qu'il n'y ait aucun moyen de créer ces deux méthodes O(1) pour un ArrayList, il existe en fait un moyen de le faire dans LinkedLists. Disons que nous voulons parcourir toute la liste en supprimant et en insérant des éléments sur notre chemin. Habituellement, vous commenciez au tout début pour chaque élément à l'aide de LinkedList, nous pourrions également "enregistrer" l'élément en cours sur lequel nous travaillons avec un Iterator. Avec l'aide de l'Iterator, nous obtenons une efficacité O(1) pour remove () et insert () lorsque vous travaillez dans une LinkedList. Je suis conscient du fait que c’est le meilleur avantage en termes de performances d’une LinkedList toujours meilleure que d’une ArrayList.

1
pietz

ArrayList étend AbstractList et implémente l'interface de liste. ArrayList est un tableau dynamique.
On peut dire qu’il a été créé pour surmonter les inconvénients des tableaux

La classe LinkedList étend AbstractSequentialList et implémente les interfaces List, Deque et Queue .
Performance
arraylist.get() est O(1) alors que linkedlist.get() est O(n) 
arraylist.add() est O(1) et linkedlist.add() est 0 (1)
arraylist.contains() est O(n) etlinkedlist.contains() est O(n) 
arraylist.next() est O(1) et linkedlist.next() est O (1)
arraylist.remove() est O(n) alors que linkedlist.remove() est O (1)
En aryliste
iterator.remove() est O (n)
alors que dans la liste chaînée 
iterator.remove()is O(1) 

0
Randhawa

L'un des tests que j'ai vus ici ne fait que le test une fois. Mais ce que j’ai remarqué, c’est que vous devez exécuter ces tests à plusieurs reprises et que leurs temps vont finalement converger. Fondamentalement, la machine virtuelle Java doit se réchauffer. Pour mon cas d'utilisation particulier, je devais ajouter/supprimer des éléments à un dernier élément d'environ 500 éléments. Lors de mes tests, LinkedList est sorti plus rapidement, avec LinkedList lié environ 50 000 NS et ArrayList environ 90 000 NS ... donnes ou prenons. Voir le code ci-dessous.

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

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

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