LinkedList o ArrayList: batalla de titanes de las colecciones Java
\n\nSi alguna vez has escrito código Java que trabaja con listas de datos, seguramente te has enfrentado al dilema: LinkedList o ArrayList? Esta pregunta es un verdadero éxito en Stack Overflow, la discuten miles de desarrolladores. Y no es para menos. Elegir la estructura de datos incorrecta puede convertir tu código rápido en una tortuga que se arrastra por un pantano. Mientras que la elección correcta puede hacer que tu aplicación sea receptiva y eficiente.
\n\nHoy no solo enumeraremos datos secos. Vamos a mirar bajo el capó de Java, entenderemos cómo funcionan estas colecciones a nivel de memoria y procesador, y daremos una respuesta clara: ¿cuándo se debe usar LinkedList y cuándo ArrayList? ¡Allá vamos!
\n\nBrevemente sobre lo principal: qué dicen los caballeros de la documentación
\n\nEmpecemos con lo básico. Ambas clases implementan la interfaz List, pero lo hacen de manera diferente.
- \n
- ArrayList es un arreglo dinámico. Almacena elementos en un bloque continuo de memoria. Cuando el arreglo se llena, crea uno nuevo de mayor tamaño y copia todos los elementos allí. \n
- LinkedList es una lista doblemente enlazada. Cada elemento (nodo) almacena referencias al elemento anterior y al siguiente. La memoria se asigna por separado para cada nodo. \n
Suena simple. Pero el diablo, como siempre, está en los detalles. Veamos el código.
\n\nBajo el capó: cómo funciona en la práctica
\n\nArrayList: velocidad de acceso, pero inserción lenta en el medio
\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ArrayListDemo {\n public static void main(String[] args) {\n List<String> arrayList = new ArrayList<>();\n \n // Agregar al final — rápido (O(1) amortizado)\n long start = System.nanoTime();\n for (int i = 0; i < 100000; i++) {\n arrayList.add("Element " + i);\n }\n long end = System.nanoTime();\n System.out.println("ArrayList add (al final): " + (end - start) / 1e6 + " ms");\n \n // Acceso por índice — instantáneo (O(1))\n start = System.nanoTime();\n String element = arrayList.get(50000);\n end = System.nanoTime();\n System.out.println("ArrayList get: " + (end - start) + " ns");\n \n // Inserción en el medio — lenta (O(n))\n start = System.nanoTime();\n arrayList.add(25000, "INSERTED");\n end = System.nanoTime();\n System.out.println("ArrayList insert (medio): " + (end - start) / 1e6 + " ms");\n }\n}\n\n¿Qué sucede aquí? Cuando insertas un elemento en el medio de un ArrayList, todos los elementos a la derecha del punto de inserción se desplazan una posición. Esta es una operación O(n). Si tienes un millón de elementos, serán un millón de operaciones de copia. Doloroso.
\n\nLinkedList: inserción — el rey, pero acceso — el paria
\n\nimport java.util.LinkedList;\nimport java.util.List;\n\npublic class LinkedListDemo {\n public static void main(String[] args) {\n List<String> linkedList = new LinkedList<>();\n \n // Agregar al final — rápido (O(1))\n long start = System.nanoTime();\n for (int i = 0; i < 100000; i++) {\n linkedList.add("Element " + i);\n }\n long end = System.nanoTime();\n System.out.println("LinkedList add (al final): " + (end - start) / 1e6 + " ms");\n \n // Acceso por índice — lento (O(n))\n start = System.nanoTime();\n String element = linkedList.get(50000);\n end = System.nanoTime();\n System.out.println("LinkedList get: " + (end - start) / 1e6 + " ms");\n \n // Inserción en el medio — rápido (O(1) después de encontrar la posición)\n start = System.nanoTime();\n // ATENCIÓN: el método add(index, element) en sí mismo sigue haciendo una búsqueda (O(n))\n linkedList