Listas enlazadas en Java: teoría y ejemplos
Aprende qué es una lista enlazada en Java con ejemplos: implementación desde cero, costes de acceso e inserción y cuándo usar LinkedList en lugar de ArrayList.

Una lista enlazada en Java es una estructura de datos de nodos: cada nodo guarda un valor y una referencia al siguiente. Permite insertar y eliminar en el inicio en tiempo constante, pero acceder por índice cuesta O(n). Se implementa desde cero con clases Nodo y ListaEnlazada, o se usa LinkedList de java.util cuando abundan las inserciones.
Listas enlazadas en Java: teoría y ejemplos
Última actualización: agosto 2026
Contenido Rápido
- Una lista enlazada guarda elementos en nodos con referencia al siguiente
- Insertar y borrar al inicio cuesta O(1); acceder por índice, O(n)
- Se implementa desde cero con clases Nodo y ListaEnlazada en Java puro
- LinkedList de java.util es la versión lista para usar, doblemente enlazada
- ArrayList gana en acceso aleatorio; LinkedList, en inserciones en los extremos
- Son la base de pilas, colas y otras estructuras de datos
Si te has preguntado cómo funciona por dentro una lista en Java, aquí está la respuesta. Las listas enlazadas son una de las estructuras de datos más importantes de la programación y el cimiento de pilas y colas. En esta guía las implementamos desde cero con Java puro y las comparamos con la clase LinkedList de la JDK.
Necesitas un JDK para seguir los ejemplos. Si aún no lo tienes, primero instala Java y vuelve. Todos los fragmentos son archivos completos que compilan con javac, así que puedes copiarlos y ejecutarlos tal cual.
Qué es una lista enlazada
Una lista enlazada es una colección de nodos conectados entre sí. Cada nodo guarda dos datos: el valor que almacena y la referencia al siguiente nodo. La cabeza es el primer nodo; desde ella se recorren las referencias hasta un nodo cuyo siguiente es null, que marca el final.
La diferencia con un array es que los elementos no ocupan posiciones contiguas: cada nodo puede vivir en cualquier sitio y la referencia mantiene el orden. Por eso insertar al inicio no obliga a mover nada: se crea el nodo, se le apunta a la cabeza actual y pasa a ser la cabeza. Cuesta lo mismo siempre.
Existen dos variantes. En una lista simple, cada nodo conoce solo a su siguiente. En una lista doblemente enlazada, cada nodo guarda además una referencia al anterior, lo que permite recorrerla en ambas direcciones. La LinkedList de la JDK es doblemente enlazada, y aquí verás ambas ideas.
Implementación desde cero en Java
La mejor forma de entender una lista enlazada es construirla. En Java moderno, un nodo se modela con un record, el mismo tipo inmutable que ya usamos en programación funcional. Este record guarda un entero y la referencia al siguiente nodo:
public record Nodo(int valor, Nodo siguiente) {
public static void main(String[] args) {
Nodo lista = new Nodo(1, new Nodo(2, new Nodo(3, null)));
Nodo actual = lista;
while (actual != null) {
System.out.println(actual.valor());
actual = actual.siguiente();
}
}
}
Ejecuta estos ejemplos en el Java Playground de CodeJa: compila y ejecuta Java en el navegador sin instalar nada. Todos los fragmentos de esta guía son Java puro, así que caben en el playground sin configuración.
Un record genera automáticamente el constructor, los métodos de acceso y equals. La lista se construye anidando nodos: el 1 apunta al 2, este al 3 y el 3 a null; el programa imprime 1, 2 y 3. Es elegante pero inmutable: para añadir o quitar elementos hace falta una estructura modificable.
Eso aporta la clase ListaEnlazada: una referencia a la cabeza, un contador de tamaño y métodos para agregar al inicio y al final, obtener, eliminar, consultar el tamaño y recorrer. Esta es la implementación completa:
import java.util.Objects;
public class ListaEnlazada<T> {
private Nodo<T> cabeza;
private int tamano;
private static class Nodo<T> {
T valor;
Nodo<T> siguiente;
Nodo(T valor) {
this.valor = valor;
}
}
public void agregarAlInicio(T valor) {
Nodo<T> nuevo = new Nodo<>(valor);
nuevo.siguiente = cabeza;
cabeza = nuevo;
tamano++;
}
public void agregarAlFinal(T valor) {
Nodo<T> nuevo = new Nodo<>(valor);
if (cabeza == null) {
cabeza = nuevo;
} else {
Nodo<T> ultimo = cabeza;
while (ultimo.siguiente != null) {
ultimo = ultimo.siguiente;
}
ultimo.siguiente = nuevo;
}
tamano++;
}
public T obtener(int indice) {
if (indice < 0 || indice >= tamano) {
throw new IndexOutOfBoundsException("Índice fuera de rango: " + indice);
}
Nodo<T> actual = cabeza;
for (int i = 0; i < indice; i++) {
actual = actual.siguiente;
}
return actual.valor;
}
public boolean eliminar(T valor) {
if (cabeza == null) {
return false;
}
if (Objects.equals(cabeza.valor, valor)) {
cabeza = cabeza.siguiente;
tamano--;
return true;
}
Nodo<T> actual = cabeza;
while (actual.siguiente != null && !Objects.equals(actual.siguiente.valor, valor)) {
actual = actual.siguiente;
}
if (actual.siguiente == null) {
return false;
}
actual.siguiente = actual.siguiente.siguiente;
tamano--;
return true;
}
public int size() {
return tamano;
}
public void recorrer() {
Nodo<T> actual = cabeza;
while (actual != null) {
System.out.print(actual.valor);
if (actual.siguiente != null) {
System.out.print(" -> ");
}
actual = actual.siguiente;
}
System.out.println();
}
public static void main(String[] args) {
ListaEnlazada<String> tareas = new ListaEnlazada<>();
tareas.agregarAlFinal("Estudiar Java");
tareas.agregarAlInicio("Preparar café");
tareas.agregarAlFinal("Repasar listas enlazadas");
tareas.recorrer();
System.out.println("Tamaño: " + tareas.size());
System.out.println("Primera: " + tareas.obtener(0));
tareas.eliminar("Estudiar Java");
tareas.recorrer();
System.out.println("Tamaño final: " + tareas.size());
}
}
Repasemos cada método, porque ahí vive el concepto.
agregarAlInicio crea el nodo y lo conecta delante: su siguiente apunta a la cabeza actual y la cabeza pasa a ser él. Como no hay que desplazar nada, cuesta tiempo constante.
agregarAlFinal recorre la lista desde la cabeza hasta el último nodo y engancha el nuevo. Cuesta O(n), porque hay que cruzar todos los elementos. Mantener una referencia a la cola lo haría O(1), pero complica el borrado del último nodo.
eliminar busca el valor y reengancha el nodo anterior con el siguiente, saltando el nodo que se borra. Si el elemento está en la cabeza, basta con moverla un nodo hacia delante. Devuelve true si borró el valor y false si no estaba.
obtener recorre la lista índice posiciones y devuelve el valor de ese nodo; es la operación que más pierde frente a un array.
size devuelve el contador y recorrer imprime los valores separados por flechas. El main lo demuestra: añade tareas, recorre, consulta, borra y vuelve a recorrer.
Análisis de costes: lista enlazada vs ArrayList
La pregunta práctica es cuánto cuesta cada operación. En una lista enlazada simple: insertar o borrar al inicio cuesta O(1); insertar al final sin referencia a la cola, O(n); buscar o acceder por índice, O(n). ArrayList, en cambio, accede por índice en O(1), porque los elementos están contiguos, pero insertar al inicio le cuesta O(n): desplaza todos los elementos.
La tabla compara las dos estructuras:
| Característica | Lista enlazada | Array |
|---|---|---|
| Acceso por índice | O(n), recorre nodos | O(1), directo |
| Inserción al inicio | O(1), cambia referencias | O(n), desplaza elementos |
| Inserción al final | O(n), recorre hasta el último | O(1) amortizado |
| Memoria | Mayor, una referencia por nodo | Menor, memoria contigua |
| Cuándo usarla | Muchas inserciones y borrados en los extremos | Acceso aleatorio y búsquedas |
La diferencia se nota con números. Este programa inserta el mismo valor al inicio de un ArrayList y de una LinkedList y las imprime:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Inserciones {
public static void main(String[] args) {
List<String> array = new ArrayList<>();
List<String> enlazada = new LinkedList<>();
array.add(0, "A"); // O(n): desplaza los elementos existentes
enlazada.add(0, "A"); // O(1): solo actualiza la referencia de la cabeza
System.out.println(array);
System.out.println(enlazada);
}
}
Ambas listas quedan con el valor A, pero el coste interno es distinto: el ArrayList desplaza los elementos existentes; la LinkedList solo cambia la referencia de la cabeza. En una sola inserción no lo notas; en cientos de miles, sí.
LinkedList de java.util: cuándo usarla
La JDK ya incluye una lista enlazada lista para usar: la clase LinkedList, documentada en el javadoc oficial de LinkedList. Es doblemente enlazada, implementa List y Deque y ofrece métodos para los extremos: addFirst, addLast, removeFirst y removeLast, todos en tiempo constante. También sirve como cola o pila.
Un ejemplo típico es un historial de navegación, donde siempre entras y sales por un extremo:
import java.util.LinkedList;
public class EjemploLinkedList {
public static void main(String[] args) {
LinkedList<String> historial = new LinkedList<>();
historial.addFirst("Inicio");
historial.addLast("Primera página");
historial.addLast("Segunda página");
System.out.println(historial);
System.out.println("Primera: " + historial.getFirst());
System.out.println("Quitada: " + historial.removeFirst());
System.out.println(historial);
}
}
¿Cuándo conviene LinkedList y cuándo ArrayList? Usa LinkedList cuando insertes o borres en los extremos, sobre todo al inicio, o cuando necesites una cola. Usa ArrayList cuando accedas por índice, ordenes o busques: su memoria contigua la hace más rápida en la práctica, aunque ambas recorran en O(n). Y si buscas por clave, la estructura adecuada es un HashMap, no una lista.
Para recorrer una LinkedList puedes usar un for-each o un stream en Java 8, igual que con cualquier colección. Para más sobre las implementaciones de List, consulta el tutorial de colecciones de Oracle.
La lista enlazada es además el cimiento de otras estructuras: una pila hace push y pop en la cabeza, y una cola usa ambos extremos. Entenderlas distingue a un desarrollador senior, y los cursos de CodeJa las practican dentro de la ruta de junior a senior.
Preguntas Frecuentes
¿Qué es una lista enlazada en Java?
Una lista enlazada es una estructura de datos de nodos, donde cada nodo guarda un valor y una referencia al siguiente. Se recorre desde la cabeza siguiendo referencias hasta un nodo cuyo siguiente es null. En Java se implementa con clases propias o con LinkedList de java.util.
¿Cuándo usar LinkedList en vez de ArrayList?
Usa LinkedList cuando insertes o borres con frecuencia en los extremos, sobre todo al inicio, o cuando necesites una cola con sus operaciones addFirst, addLast, removeFirst y removeLast. Usa ArrayList cuando predomine el acceso por índice o la búsqueda: su memoria contigua la hace más rápida en la práctica.
¿Cómo se recorre una lista enlazada?
Se parte de la cabeza y se sigue la referencia siguiente en un bucle hasta un nodo cuyo siguiente sea null. En Java sirven también el for-each y los streams, porque LinkedList implementa List. El recorrido completo cuesta O(n).
¿Cuál es la complejidad de una lista enlazada?
Insertar o borrar al inicio cuesta O(1). Insertar al final sin referencia a la cola, buscar por valor y acceder por índice cuestan O(n). La ventaja frente a un array está en los extremos, no en la búsqueda.
¿Para qué se usan las listas enlazadas?
Son la base de pilas, colas y otras estructuras, y se usan con muchas inserciones y borrados en los extremos sin reasignar memoria. También sirven para tablas hash con encadenamiento y listas de adyacencia de grafos.
Artículos Relacionados
Newsletter Semanal de Java
Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.
🔒 Sin spam. Cancela cuando quieras.