Saltar a contenido

📚 Iteradores en Java (Iterator y ListIterator)

Después de aprender List, ArrayList y LinkedList, necesitamos una forma segura y estándar de recorrer colecciones.
Para eso Java proporciona los iteradores.

Aunque el estándar para recorrer colecciones es el bucle for-each (el rey 👑), ya que es más limpio, legible y menos propenso a errores.

Los iteradores son necesarios cuando queremos hacer cosas "más delicadas":

  • 🔹 eliminar mientras recorres,
  • 🔹 añadir mientras recorres,
  • 🔹 control más fino del recorrido: parar manualmente, avanzar paso a paso, ir hacia atrás.

🔁 ¿Qué es un Iterator?

Un iterador es un objeto que permite recorrer los elementos de una colección uno a uno.

La interfaz principal es:

Iterator<E>

📌 Permite recorrer colecciones como:

  • ArrayList
  • LinkedList
  • HashSet
  • TreeSet

⚙️ Métodos principales de Iterator

Método Qué hace
hasNext() Indica si quedan elementos por recorrer
next() Devuelve el elemento a la derecha y avanza el iterador a la siguiente posición
remove() Elimina el último elemento devuelto por next()

🧪 Ejemplo básico

public class EjemploIterator {

    public static void main(String[] args) {

        List<String> nombres = new ArrayList<>();

        nombres.add("Ana");
        nombres.add("Luis");
        nombres.add("Pedro");

        Iterator<String> it = nombres.iterator();

        while (it.hasNext()) {
            String nombre = it.next();
            System.out.println(nombre);
        }
    }
}

Salida:

Ana
Luis
Pedro


🧠 Cómo funciona internamente

El iterador mantiene una posición interna de recorrido.

Al crearlo, está antes del primer elemento:

[it][Ana][Luis][Pedro]

posición actual

Cada vez que llamamos a next():

  • 1️⃣ devuelve el siguiente elemento disponible (a la derecha del iterador)
  • 2️⃣ después avanza a la siguiente posición del iterador

[Ana][it][Luis][Pedro]

  • primer next() → devuelve Ana y el iterador avanza
  • segundo next() → devuelve Luis y el iterador avanza

Cuando creas el iterador con:

Iterator<String> it = lista.iterator();

El iterador está al inicio (se representa con "|"):

| elemento0 elemento1 elemento2

👉 Es decir, está antes del primer elemento. Entonces, ¿cuándo apunta al primer elemento?

Cuando hacemos:

it.next();

Entonces:
- Devuelve el elemento a la derecha del iterador
- Avanza a la siguiente posición interna del iterador

Ahora sí:

elemento0 | elemento1 elemento2

Note

Nunca llames a next() sin comprobar hasNext()


❗ Error típico al eliminar

Intentas eliminar un elemento antes de hacer next():

Iterator<String> it = lista.iterator();
it.remove(); // ❌ ERROR

👉 Lanza excepción (IllegalStateException) porque:

  • aún no has hecho next()
  • no hay “último elemento” que eliminar

⚠️ Se DEBE USAR hasNext() antes de next()

El método next() SOLO puede llamarse si existe un elemento siguiente.

👉 Si llamamos a next() cuando no quedan elementos, se produce una excepción:

NoSuchElementException

✅ Uso correcto

Iterator<String> it = lista.iterator();

while (it.hasNext()) { //compruebo si quedan elementos
    String elemento = it.next();
    System.out.println(elemento);
}

  • hasNext() → “¿queda alguien?”
  • next() → “haz pasar al siguiente”

👉 Si no queda nadie y llamas a next(), ocurre un error.


❗ Error común al recorrer colecciones

❌ Incorrecto:

for(String nombre : nombres){
    if(nombre.equals("Luis")){
        nombres.remove(nombre);
    }
}

Provoca:

ConcurrentModificationException --> indica que has modificado (add/remove) una colección mientras la estabas recorriendo de una forma no permitida.


✅ Forma correcta con Iterator

Si quieres eliminar un ítem de una colección mientras la recorres, usa el propio iterador:

Iterator<String> it = nombres.iterator();

while(it.hasNext()) {

    String nombre = it.next();

    if(nombre.equals("Luis")){
        it.remove();
    }
}

Esto es seguro porque el iterador controla las modificaciones.


🔄 for-each usa internamente un Iterator

for(String nombre : nombres){
    System.out.println(nombre);
}

Internamente Java hace algo parecido a:

Iterator<String> it = nombres.iterator();

while(it.hasNext()){
    String nombre = it.next();
}

🔁 ListIterator

Las listas (List) tienen un iterador más avanzado:

ListIterator

Solo funciona con:

  • ArrayList
  • LinkedList

⭐ Ventajas de ListIterator

Permite:

✔ recorrer la lista hacia delante y hacia atrás
modificar elementos
insertar elementos durante el recorrido


⚙️ Métodos principales

Método Qué hace
hasNext() Hay siguiente elemento
next() Devuelve siguiente
hasPrevious() Hay elemento anterior
previous() Devuelve anterior
remove() Elimina el último elemento devuelto
add(E e) Inserta elemento
set(E e) Modifica elemento actual
nextIndex() índice del elemento que devolvería next()
previousIndex() índice del elemento que devolvería previous()

🧪 Ejemplo con ListIterator

List<String> nombres = new ArrayList<>();

nombres.add("Ana");
nombres.add("Luis");
nombres.add("Pedro");

ListIterator<String> it = nombres.listIterator();

while(it.hasNext()){
    System.out.println(it.next());
}

🔁 Recorrer la lista hacia atrás

while(it.hasPrevious()){
    System.out.println(it.previous());
}

➕ Insertar elementos durante el recorrido

ListIterator<String> it = nombres.listIterator();

while(it.hasNext()){

    String nombre = it.next();

    if(nombre.equals("Luis")){
        it.add("Carlos");
    }
}

Resultado:

[Ana, Luis, Carlos, Pedro]


↔️ Recorrer una lista hacia delante y hacia detrás con ListIterator

Una de las ventajas más importantes de ListIterator es que permite recorrer una lista en ambos sentidos.

💡 Recuerda que Iterator o ListIterator no “apunta” exactamente a un elemento, sino a una posición entre elementos de la lista.

Ejemplo:

public class Principal {
    public static void main(String[] args) {
        List<String> nombres = new ArrayList<>();
        nombres.add("Ana");
        nombres.add("Luis");
        nombres.add("Marta");
        nombres.add("Pedro");

        ListIterator<String> it = nombres.listIterator();
        Scanner sc = new Scanner(System.in);

        int opcion;
        boolean haciaDelante = false;
        boolean haciaDetras = false;

        do {
            System.out.println("""
            --- MENÚ ---
            1. Ir al siguiente nombre
            2. Ir al nombre anterior
            3. Salir
            Elige una opción: """);
            opcion = sc.nextInt();

            switch (opcion) {
                case 1:
                    if (haciaDetras) {
                        // Si antes iba hacia detrás, recolocamos el iterador
                        if (it.hasNext()) {
                            it.next();
                        }
                        haciaDetras = false;
                    }

                    if (it.hasNext()) {
                        System.out.println("Actual: " + it.next());
                        haciaDelante = true;
                    } else {
                        System.out.println("Ya estás al final de la lista.");
                    }
                    break;

                case 2:
                    if (haciaDelante) {
                        // Si antes iba hacia delante, recolocamos el iterador
                        if (it.hasPrevious()) {
                            it.previous();
                        }
                        haciaDelante = false;
                    }

                    if (it.hasPrevious()) {
                        System.out.println("Actual: " + it.previous());
                        haciaDetras = true;
                    } else {
                        System.out.println("Ya estás al principio de la lista.");
                    }
                    break;

                case 3:
                    System.out.println("Saliendo...");
                    break;

                default:
                    System.out.println("Opción no válida.");
            }

        } while (opcion != 3);

        sc.close();
    }
}
  • next() devuelve el elemento de la derecha y avanza
  • previous() devuelve el elemento de la izquierda y retrocede
  • si ibas hacia delante y ahora quieres ir hacia detrás, haces un previous() de ajuste
  • si ibas hacia detrás y ahora quieres ir hacia delante, haces un next() de ajuste

🧠 Resumen

Método Iterator ListIterator
Tipo de colecciones Cualquier colección (List, Set, etc.) Solo List
hasNext()
next()
remove()
add()
set()
previous()

💡 Idea clave

Todas las colecciones se pueden recorrer con iteradores.

Iterator permite recorrer y borrar; ListIterator además permite insertar, modificar y retroceder.