ArrayList vs LinkedList: Rendimiento Real frente a Teoría
ArrayList usa un array continuo en memoria que aprovecha la caché L1/L2 de la CPU. LinkedList usa nodos dispersos con punteros que degradan el rendimiento por continuos cache misses.
En una línea List<String> elementos = new ArrayList<>(100); con capacidad inicial reservada.
// Lista: ArrayList con capacidad inicial si conoces el tamaño
List<TareaDto> dtos = new ArrayList<>(entidades.size());
for (var e : entidades) dtos.add(TareaMapper.aDto(e));
// Cola FIFO y pila LIFO: ArrayDeque (sin nodos, array circular)
Deque<String> cola = new ArrayDeque<>();
cola.offerLast("a");
cola.offerLast("b");
cola.pollFirst(); // "a"
Deque<String> pila = new ArrayDeque<>();
pila.push("x");
pila.push("y");
pila.pop(); // "y"
// Borrar mientras recorres: removeIf evita ConcurrentModificationException
dtos.removeIf(TareaDto::completada);
Cómo funciona por dentro
ArrayList guarda las referencias en un array contiguo. Acceder por índice es O(1) y recorrerla aprovecha la caché de la CPU, porque los elementos están juntos en memoria. Cuando se llena, crea un array 1,5 veces mayor y copia, lo que reparte el coste (O(1) amortizado por inserción al final). Insertar en medio exige desplazar elementos con System.arraycopy, una operación muy optimizada.
LinkedList crea un nodo por elemento con punteros al anterior y al siguiente. Cada nodo ocupa unos 24-32 bytes adicionales y está disperso por el heap, así que recorrerla provoca fallos de caché constantemente. La inserción en medio es O(1) solo si ya tienes el nodo; llegar hasta él cuesta O(n).
Conclusión práctica: usa ArrayList por defecto, y si sabes el tamaño aproximado, pásalo al constructor. Si necesitas una cola o pila, usa ArrayDeque, no LinkedList ni la antigua Stack.
Se conecta con Memoria de la CPUComplejidad O(1) vs O(n)Listas