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?
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.
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.
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)
ArrayList
est ce que vous voulez. LinkedList
est presque toujours un bogue (de performance).
Pourquoi LinkedList
est nul:
ArrayList
était utilisé.ArrayList
, elle sera probablement beaucoup plus lente de toute façon.LinkedList
dans le code source car c'est probablement le mauvais choix.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.
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)
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.
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)
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 .
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);
}
}
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:
=== ArrayList ===
=== LinkedList ===
ajouter (E e)
add (int index, élément E)
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.):
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.
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.
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.
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.
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é.
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. :)
Comparons LinkedList et ArrayList w.r.t. paramètres ci-dessous:
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.
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é.
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).
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).
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.
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.
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).
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).
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.
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.
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.
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?
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 |
|--------------------------------------|
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.
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.
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
).(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.
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.
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é.
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.
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.
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 .
Performancearraylist.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 arylisteiterator.remove()
est O (n)
alors que dans la liste chaînée iterator.remove()
is O(1)
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;
}