Resumen ArrayList
con ArrayDeque
son preferibles en muchos más casos de uso que LinkedList
. Si no está seguro, empiece por ArrayList
.
TLDR, en ArrayList acceder a un elemento lleva un tiempo constante [O(1)] y añadir un elemento lleva un tiempo O(n) [en el peor de los casos]. En LinkedList, añadir un elemento lleva un tiempo O(n) y acceder a él también lleva un tiempo O(n), pero LinkedList utiliza más memoria que ArrayList.
LinkedList
y ArrayList
son dos implementaciones diferentes de la interfaz List. LinkedList
lo implementa con una lista doblemente enlazada. ArrayList
lo implementa con un Array que se redimensiona dinámicamente.
Al igual que con las operaciones estándar de listas enlazadas y arrays, los distintos métodos tendrán tiempos de ejecución algorítmica diferentes.
Para LinkedList<E>
get(int index)
es O(n) (con n/4 pasos en promedio), pero O(1) cuando index = 0
o index = list.size() - 1
(en este caso, también puede utilizar getFirst()
y getLast()
). Uno de los principales beneficios de LinkedList<E>
add(int index, E element)
es O(n) (con n/4 pasos en promedio), pero O(1) cuando index = 0
o index = list.size() - 1
(en este caso, también puede utilizar addFirst()
y addLast()
/ add()
). Uno de los principales beneficios de LinkedList<E>
remove(int index)
es O(n) (con n/4 pasos en promedio), pero O(1) cuando index = 0
o index = list.size() - 1
(en este caso, también puede utilizar removeFirst()
y removeLast()
). Uno de los principales beneficios de LinkedList<E>
Iterator.remove()
es O(1) . Uno de los principales beneficios de LinkedList<E>
ListIterator.add(E element)
es O(1) . Uno de los principales beneficios de LinkedList<E>
Nota: Muchas de las operaciones necesitan n/4 pasos de media, constante número de pasos en el mejor de los casos (por ejemplo, índice = 0), y n/2 pasos en el peor de los casos (la mitad de la lista)
Para ArrayList<E>
get(int index)
es O(1) . Principal beneficio de ArrayList<E>
add(E element)
es O(1) amortizado, pero O(n) en el peor de los casos, ya que el Array debe ser redimensionado y copiado
add(int index, E element)
es O(n) (con n/2 pasos de media)
remove(int index)
es O(n) (con n/2 pasos de media)
Iterator.remove()
es O(n) (con n/2 pasos de media)
ListIterator.add(E element)
es O(n) (con n/2 pasos de media)
Nota: Muchas de las operaciones necesitan n/2 pasos de media, constante número de pasos en el mejor de los casos (fin de la lista), n pasos en el peor de los casos (inicio de la lista)
LinkedList<E>
permite realizar inserciones o extracciones en tiempo constante utilizando iteradores pero sólo el acceso secuencial de los elementos. En otras palabras, se puede recorrer la lista hacia delante o hacia atrás, pero encontrar una posición en la lista lleva un tiempo proporcional al tamaño de la lista. Javadoc dice "las operaciones que indexan en la lista recorrerán la lista desde el principio o el final, lo que esté más cerca" Así que esos métodos son O(n) ( n/4 pasos) en promedio, aunque O(1) para index = 0
.
ArrayList<E>
por otro lado, permiten un rápido acceso de lectura aleatorio, por lo que se puede coger cualquier elemento en tiempo constante. Pero añadir o eliminar desde cualquier lugar que no sea el final requiere desplazar todos los últimos elementos, ya sea para hacer una abertura o rellenar el hueco. Además, si añades más elementos que la capacidad de la matriz subyacente, se asigna una nueva matriz (1,5 veces el tamaño), y la matriz antigua se copia en la nueva, por lo que añadir a una matriz ArrayList
es O(n) en el peor de los casos, pero constante en la media.
Así que dependiendo de las operaciones que pretendas hacer, deberás elegir las implementaciones en consecuencia. Iterar sobre cualquiera de los dos tipos de Lista es prácticamente igual de barato. (Iterar sobre una ArrayList
es técnicamente más rápido, pero a menos que estés haciendo algo realmente sensible al rendimiento, no deberías preocuparte por esto - ambos son constantes).
Las principales ventajas de utilizar un LinkedList
surgen cuando se reutilizan los iteradores existentes para insertar y eliminar elementos. Estas operaciones se pueden realizar en O(1) cambiando la lista sólo localmente. En una lista de Array, el resto del Array necesita ser movido (es decir, copiado). Por otro lado, buscar en un LinkedList
significa seguir los enlaces en O(n) ( n/2 pasos) para el peor de los casos, mientras que en un ArrayList
la posición deseada se puede calcular matemáticamente y acceder a ella en O(1) .
Otra ventaja de utilizar un LinkedList
surge cuando se agrega o elimina de la cabeza de la lista, ya que esas operaciones son O(1) mientras que ellos son O(n) para ArrayList
. Tenga en cuenta que ArrayDeque
puede ser una buena alternativa a LinkedList
para añadir y quitar de la cabeza, pero no es un List
.
Además, si tienes listas grandes, ten en cuenta que el uso de la memoria también es diferente. Cada elemento de una lista LinkedList
tiene más sobrecarga, ya que también se almacenan los punteros a los elementos siguientes y anteriores. ArrayLists
no tienen esta sobrecarga. Sin embargo, ArrayLists
ocupan toda la memoria asignada para la capacidad, independientemente de que se hayan añadido realmente elementos.
La capacidad inicial por defecto de un ArrayList
es bastante pequeño (10 de Java 1.4 - 1.8). Pero como la implementación subyacente es un Array, el Array debe ser redimensionado si añades muchos elementos. Para evitar el alto coste de redimensionar cuando sabes que vas a añadir muchos elementos, construye el ArrayList
con una mayor capacidad inicial.
Si se utiliza la perspectiva de las estructuras de datos para entender las dos estructuras, una LinkedList es básicamente una estructura de datos secuencial que contiene un Nodo de cabecera. El Nodo es una envoltura para dos componentes: un valor de tipo T [aceptado a través de los genéricos] y otra referencia al Nodo vinculado a él. Por tanto, podemos afirmar que es una estructura de datos recursiva (un Nodo contiene otro Nodo que tiene otro Nodo y así sucesivamente...). La adición de elementos toma un tiempo lineal en LinkedList como se ha dicho anteriormente.
Un ArrayList, es un Array que puede crecer. Es igual que un Array normal. Cuando se añade un elemento en el índice i, se crea otro Array con un tamaño que es 1 mayor que el tamaño anterior (así que en general, cuando se añaden n elementos a un ArrayList, se crea un nuevo Array de tamaño anterior más n). A continuación se copian los elementos del Array anterior al nuevo y los elementos que se van a añadir se colocan también en los índices especificados.
7 votos
Véase también: Matriz frente a lista enlazada
7 votos
Basta con ver la cita del autor de LinkedList stackoverflow.com/a/42529652/2032701 y tendrás una idea práctica del asunto.