📚 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()→ devuelveAnay el iterador avanza - segundo
next()→ devuelveLuisy 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 avanzaprevious()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.
Iteratorpermite recorrer y borrar;ListIteratorademás permite insertar, modificar y retroceder.