Oui, c'est un sujet ancien, mais j'ai encore des confusions.
En Java, les gens disent:
ArrayList est plus rapide que LinkedList si j'accède aléatoirement à ses éléments. Je pense que l'accès aléatoire signifie "donnez-moi le nième élément". Pourquoi ArrayList est plus rapide?
LinkedList est plus rapide que ArrayList pour la suppression. Je comprends celui-ci. ArrayList est plus lent car le tableau de sauvegarde interne doit être réalloué. Une explication de code:
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.remove("b");
System.out.println(list.get(1)); //output "c"
LinkedList est plus rapide que ArrayList pour l'insertion. Que signifie insertion ici? Si cela signifie déplacer certains éléments vers l'arrière, puis les placer dans l'emplacement vide du milieu, ArrayList doit être plus lent que LinkedList. Si insertion signifie seulement une opération d'ajout (objet), comment cela pourrait-il être lent?
ArrayList est plus rapide que LinkedList si j'accède aléatoirement à ses éléments. Je pense que l'accès aléatoire signifie "donnez-moi le nième élément". Pourquoi ArrayList est plus rapide?
ArrayList
possède des références directes à chaque élément de la liste, ce qui lui permet d’obtenir le n-ième élément en temps constant. LinkedList
doit parcourir la liste depuis le début pour atteindre le n-ième élément.
LinkedList est plus rapide que ArrayList pour la suppression. Je comprends celui-ci. ArrayList est plus lent car le tableau de sauvegarde interne doit être réalloué.
ArrayList
est plus lent car il doit copier une partie du tableau afin de supprimer le slot devenu libre. Si la suppression est effectuée à l'aide de l'API ListIterator.remove()
, LinkedList
ne doit manipuler que quelques références; si la suppression est effectuée par valeur ou par index, LinkedList
doit potentiellement analyser d'abord la liste complète pour trouver le ou les éléments à supprimer.
Si cela signifie déplacer certains éléments vers l'arrière puis placer l'élément dans l'emplacement vide du milieu, ArrayList devrait être plus lent.
Oui, c'est ce que cela signifie. ArrayList
est en effet plus lent que LinkedList
car il doit libérer un emplacement au milieu du tableau. Cela implique de déplacer certaines références et, dans le pire des cas, de réaffecter l’ensemble du tableau. LinkedList
doit juste manipuler certaines références.
Ignorer cette réponse pour l'instant. Les autres réponses, en particulier celle de aix, sont généralement correctes. Sur le long terme, c'est le moyen de parier. Et si vous avez suffisamment de données (sur un repère sur une machine, il semble y avoir environ un million d'entrées) ArrayList et LinkedList fonctionnent actuellement comme annoncé. Cependant, il y a de beaux points qui s'appliquent au début du 21ème siècle.
D'après mes tests, la technologie informatique moderne semble donner un avantage énorme aux tableaux. Les éléments d'un tableau peuvent être déplacés et copiés à une vitesse incroyable. En conséquence, les tableaux et ArrayList, dans la plupart des situations pratiques, surperforeront LinkedList lors des insertions et des suppressions, souvent de façon spectaculaire. En d'autres termes, ArrayList bat LinkedList à son propre jeu.
L'inconvénient de ArrayList est qu'il a tendance à s'accrocher à l'espace mémoire après les suppressions, où LinkedList cède de l'espace tout comme des entrées.
Le plus grand inconvénient des tableaux et de ArrayList est qu’ils fragmentent la mémoire libre et surchargent le garbage collector. Au fur et à mesure que ArrayList se développe, il crée de nouveaux tableaux plus grands, copie l'ancien tableau dans le nouveau et libère l'ancien. La mémoire se remplit de gros morceaux contigus de mémoire libre qui ne sont pas assez gros pour la prochaine allocation. Finalement, il n'y a pas d'espace approprié pour cette allocation. Même si 90% de la mémoire est libre, aucun élément individuel n'est assez gros pour faire le travail. Le GC travaillera avec acharnement pour déplacer les choses, mais s'il faut trop de temps pour réorganiser l'espace, il lève une exception OutOfMemoryException. S'il n'abandonne pas, il peut quand même ralentir votre programme.
Le pire est que ce problème peut être difficile à prévoir. Votre programme fonctionnera bien une fois. Ensuite, avec un peu moins de mémoire disponible, sans avertissement, cela ralentit ou s’arrête.
LinkedList utilise de petits morceaux de mémoire délicats et GC adore ça. Il fonctionne toujours bien lorsque vous utilisez 99% de votre mémoire disponible.
Par conséquent, en règle générale, utilisez ArrayList pour des ensembles de données plus petits dont la plupart des contenus ne risquent pas d'être supprimés, ou lorsque vous avez un contrôle strict sur la création et la croissance. (Par exemple, créer un ArrayList qui utilise 90% de mémoire et l'utiliser sans le remplir pendant toute la durée du programme est acceptable. Créer et libérer en permanence des occurrences ArrayList utilisant 10% de mémoire vous tuerait.) Sinon, utilisez LinkedList. (ou une carte de quelque sorte si vous avez besoin d'un accès aléatoire). Si vous avez des collections très volumineuses (plus de 100 000 éléments, par exemple), que vous ne craignez pas le GC, que vous planifiez de nombreuses insertions et suppressions et qu'il n'y a pas d'accès aléatoire, exécutez quelques tests pour voir ce qui est le plus rapide.
La classe ArrayList
est une classe wrapper pour un tableau. Il contient un tableau interne.
public ArrayList<T> {
private Object[] array;
private int size;
}
Un LinkedList
est une classe wrapper pour une liste liée, avec un nœud interne pour gérer les données.
public LinkedList<T> {
class Node<T> {
T data;
Node next;
Node prev;
}
private Node<T> first;
private Node<T> last;
private int size;
}
Notez que le code actuel est utilisé pour montrer comment la classe peut être, pas l'implémentation réelle. En connaissant la mise en œuvre, nous pouvons procéder à une analyse plus poussée:
ArrayList est plus rapide que LinkedList si j'accède aléatoirement à ses éléments. Je pense que l'accès aléatoire signifie "donnez-moi le nième élément". Pourquoi ArrayList est plus rapide?
Temps d'accès pour ArrayList: O (1). Temps d'accès pour LinkedList: O (n).
Dans un tableau, vous pouvez accéder à n’importe quel élément en utilisant array[index]
, dans une liste chaînée, vous devez parcourir toute la liste en commençant par first
jusqu'à ce que vous obteniez l'élément dont vous avez besoin.
LinkedList est plus rapide que ArrayList pour la suppression. Je comprends celui-ci. ArrayList est plus lent car le tableau de sauvegarde interne doit être réalloué.
Heure de suppression de ArrayList: Temps d'accès + O (n). Heure de suppression de LinkedList: Temps d'accès + O (1).
ArrayList doit déplacer tous les éléments de array[index]
à array[index-1]
en commençant par l'élément à supprimer index. La LinkedList doit naviguer jusqu'à cet élément, puis effacer ce nœud en le dissociant de la liste.
LinkedList est plus rapide que ArrayList pour la suppression. Je comprends celui-ci. ArrayList est plus lent car le tableau de sauvegarde interne doit être réalloué.
Temps d'insertion pour ArrayList: O (n). Temps d'insertion pour LinkedList: O (1).
Pourquoi la liste de tableaux peut prendre O (n)? Parce que lorsque vous insérez un nouvel élément et que le tableau est plein, vous devez créer un nouveau tableau avec plus de taille (vous pouvez calculer la nouvelle taille avec une formule telle que 2 * size ou 3 * size/2). La LinkedList ajoute simplement un nouveau noeud à côté du dernier.
Cette analyse ne se limite pas à Java mais à d’autres langages de programmation tels que C, C++ et C #.
Plus d'infos ici:
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 un ArrayList, vous obtenez l'élément de 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é. Supprimer ou insérer est constant une fois que nous y sommes arrivés, car nous n'avons qu'à 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.
Bonus: Bien qu'il n'y ait aucun moyen de rendre 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.
Réponse à 1: ArrayList utilise un tableau sous le capot. Accéder à un membre d'un objet ArrayList est aussi simple que d'accéder au tableau à l'index fourni, en supposant que l'index se situe dans les limites du tableau de sauvegarde. Une LinkedList doit parcourir ses membres pour accéder au nième élément. C'est O(n) pour une LinkedList, par opposition à O(1) pour ArrayList.
Dans une LinkedList, les éléments ont une référence à l'élément avant et après celui-ci. Dans une ArrayList, la structure de données n'est qu'un tableau.
Une LinkedList doit parcourir plus de N éléments pour obtenir le Nième élément. Une ArrayList doit seulement renvoyer l'élément N du tableau de sauvegarde.
Le tableau de sauvegarde doit être réaffecté pour la nouvelle taille et le tableau copié, ou chaque élément après l'élément supprimé doit être déplacé vers le haut pour remplir l'espace vide. Une LinkedList doit simplement définir la référence précédente sur l'élément après l'élément supprimé à celle précédant l'élément supprimé et la référence suivante sur l'élément avant l'élément supprimé à l'élément après l'élément supprimé. Plus long à expliquer, mais plus rapide à faire.
Même raison que la suppression ici.
ArrayList
LinkedList: -
J'en viens maintenant à vos questions: -
1) ArrayList enregistre les données en fonction des index et implémente l’interface RandomAccess, qui est une interface de marqueur qui offre la possibilité d’une extraction aléatoire vers ArrayList mais LinkedList n’implémente pas l’interface RandomAccess. C’est pourquoi ArrayList est plus rapide que LinkedList.
2) La structure de données sous-jacente de LinkedList est une liste doublement chaînée. L’insertion et la suppression au milieu est donc très facile dans LinkedList, car elle n’a pas à décaler chaque élément pour chaque opération de suppression et d’insertion, comme ArrayList (qui est déconseillé si notre opération est l’insertion et la suppression au milieu car plusieurs opérations de décalage sont effectuées en interne).
Source
ArrayList: La classe ArrayList étend AbstractList et implémente l'interface List et RandomAccess (interface de marqueur). ArrayList prend en charge les tableaux dynamiques pouvant se développer en fonction des besoins. Cela nous donne la première itération sur les éléments.
LinkedList: Une LinkedList est classée par position d'index, comme ArrayList, sauf que les éléments sont liés par un lien double. Ce lien vous donne de nouvelles méthodes (au-delà de ce que vous obtenez de l'interface List) pour ajouter et supprimer du début ou de la fin, ce qui en fait un choix facile pour l'implémentation d'une pile ou d'une file d'attente. Gardez à l'esprit qu'une LinkedList peut itérer plus lentement qu'une ArrayList, mais c'est un bon choix lorsque vous avez besoin d'une insertion et d'une suppression rapides. À partir de Java 5, la LinkedList La classe a été améliorée pour implémenter l'interface Java.util.Queue et prend désormais en charge les méthodes de file d'attente courantes: peek (), poll () et offer ().
ArrayList: ArrayList a une structure semblable à un tableau, il a une référence directe à chaque élément. Ainsi, l'accès au rendu est rapide dans ArrayList.
LinkedList: Dans LinkedList pour obtenir le nième élemnt, vous devez parcourir toute la liste, cela prend du temps comparé à ArrayList. Chaque élément a un lien vers son élément précédent et nid, la suppression est donc rapide.
Je veux ajouter un élément d'information supplémentaire sur la différence de performance.
Nous savons déjà que, du fait que ArrayList
la mise en oeuvre est soutenue par un Object[]
_ prend en charge l’accès aléatoire et le redimensionnement dynamique et la mise en œuvre de LinkedList
utilise des références en tête et en bout pour le parcourir. Il n'a pas de capacité d'accès aléatoire, mais il prend également en charge le redimensionnement dynamique.
La première chose est qu'avec une ArrayList, vous pouvez accéder immédiatement à l'index, alors qu'avec une LinkedList, vous pouvez parcourir la chaîne d'objets.
Deuxièmement, l'insertion dans ArrayList est généralement plus lente car elle doit croître une fois que vous atteignez ses limites. Il devra créer un nouveau tableau plus grand et copier les données de celui d'origine.
Mais la chose intéressante est que lorsque vous créez un ArrayList qui est déjà assez énorme Pour s’adapter à toutes vos insertions, il ne sera évidemment pas nécessaire de copier le tableau. Ajouter à cela sera encore plus rapide qu'avec LinkedList, car LinkedList devra gérer ses pointeurs, alors qu'énorme ArrayList ne définit que la valeur d'un index donné.
Découvrez plus différences ArrayList et LinkedList .