Skip to content

Iterable: La Capacidad de ser Recorrido

Introducción: El Reproductor de Música

¡Hola, equipo! 🎶

Hemos hablado mucho del Iterator (el mecanismo para pasar de canción). Pero, para que puedas usar un iterador, necesitas algo que sea "iterable".

Imaginad un MP3 (u ordenador, móvil...). El dispositivo en sí es un objeto Iterable. Significa que tiene la capacidad de ofrecerte una lista de canciones para que tú vayas pulsando "Siguiente".

Si una clase en Java implementa la interfaz Iterable, le está gritando al compilador: "¡Eh! Tengo cosas dentro que se pueden recorrer una a una. ¡Puedes usarme en un bucle for-each!".

Sin esta interfaz, el famoso bucle for (Elemento e : coleccion) no funcionaría. Hoy vamos a ver cómo crear nuestras propias clases recorribles.


Conceptos Fundamentales

Definición: Interfaz Iterable

Ubicada en java.lang.Iterable. Es la interfaz raíz de todas las colecciones (excepto Map). * Contrato: Obliga a la clase a tener un método llamado iterator() que devuelva un objeto Iterator. * Superpoder: Cualquier objeto que implemente Iterable puede ser el objetivo de la sentencia "for-each" (:) de Java.

Diferencia Clave

Iterable: Es el contenedor (la caja de galletas). Iterator: Es la mano que saca las galletas una a una.

El "Azúcar Sintáctico" del For-Each

Cuando escribís esto:

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

El compilador de Java, en secreto, lo traduce a esto:

Iterator<String> it = nombres.iterator();
while (it.hasNext()) {
    String nombre = it.next();
    System.out.println(nombre);
}
¡Por eso es tan importante implementar Iterable!



Desarrollo y Ejemplo Práctico

Vamos a crear algo que no sea una lista estándar de Java, pero que se pueda recorrer como si lo fuera.

Imaginad que creamos una clase EquipoProyecto para guardar a los alumnos de un grupo de DAW. Queremos poder hacer un for (Alumno a : miEquipo) directamente.

Paso 1: La clase de datos (Alumno)

Un POJO (Plain Old Java Object) sencillo.

public class Alumno {
    private String nombre;
    private int nota;

    public Alumno(String nombre, int nota) {
        this.nombre = nombre;
        this.nota = nota;
    }

    @Override
    public String toString() {
        return nombre + " (" + nota + ")";
    }
}

Paso 2: La clase Iterable (EquipoProyecto)

Aquí ocurre la magia. Implementamos Iterable<Alumno>.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

// 1. Decimos que esta clase se puede recorrer devolviendo 'Alumnos'
public class EquipoProyecto implements Iterable<Alumno> {

    private String nombreEquipo;
    // Usamos una lista interna para guardar los datos, pero es privada.
    private List<Alumno> integrantes;

    public EquipoProyecto(String nombreEquipo) {
        this.nombreEquipo = nombreEquipo;
        this.integrantes = new ArrayList<>();
    }

    public void agregarIntegrante(Alumno a) {
        integrantes.add(a);
    }

    // 2. Este es el método OBLIGATORIO de la interfaz Iterable.
    // Debemos devolver un Iterator<Alumno>.
    @Override
    public Iterator<Alumno> iterator() {
        // Truco de experto: Como ArrayList ya sabe crear iteradores,
        // delegamos el trabajo en él. ¡No reinventamos la rueda!
        return integrantes.iterator();
    }

    // Método extra para mostrar nombre del equipo
    public String getNombre() { return nombreEquipo; }
}

Paso 3: Probándolo en el Main

public class MainIterable {
    public static void main(String[] args) {
        // Creamos nuestro objeto personalizado
        EquipoProyecto equipoAlpha = new EquipoProyecto("Alpha DAW");

        equipoAlpha.agregarIntegrante(new Alumno("Lucía", 9));
        equipoAlpha.agregarIntegrante(new Alumno("Marcos", 7));
        equipoAlpha.agregarIntegrante(new Alumno("Sofía", 10));

        System.out.println("Integrantes del equipo " + equipoAlpha.getNombre() + ":");

        // ¡MAGIA! ✨
        // Podemos usar el bucle for-each directamente sobre nuestro objeto 'equipoAlpha'
        // Esto funciona SOLO porque EquipoProyecto implementa Iterable.
        for (Alumno a : equipoAlpha) {
            System.out.println(" - " + a);
        }

        // Si no implementáramos Iterable, tendríamos que haber hecho:
        // equipoAlpha.getIntegrantes()... exponiendo la lista interna (mala encapsulación).
    }
}



Diagrama Mermaid: La Relación Iterable-Iterator

Visualizad cómo el bucle for interactúa con vuestra clase.

sequenceDiagram
    participant Main as Bucle For-Each
    participant Equipo as EquipoProyecto (Iterable)
    participant Interno as ArrayList Interno
    participant It as Iterator

    Main->>Equipo: Dame tu .iterator()
    Equipo->>Interno: .iterator() (Delegación)
    Interno-->>Equipo: new Iterator()
    Equipo-->>Main: Iterator listo

    loop Mientras hasNext() sea true
        Main->>It: .next()
        It-->>Main: Alumno
        Main->>Main: Imprimir Alumno
    end

¿Y si quiero una lógica de recorrido personalizada?

En el ejemplo anterior hemos delegado en el ArrayList. Pero, ¿y si quisiéramos un iterador que recorriera el equipo al revés o que solo mostrara los aprobados? Tendríamos que crear nuestra propia clase Iterator anónima.

(Solo para curiosos nivel avanzado):

@Override
public Iterator<Alumno> iterator() {
    return new Iterator<Alumno>() {
        private int indiceActual = 0;

        @Override
        public boolean hasNext() {
            // Lógica personalizada: Hay siguiente si el índice no se sale
            return indiceActual < integrantes.size();
        }

        @Override
        public Alumno next() {
            // Devolvemos y avanzamos
            return integrantes.get(indiceActual++);
        }
    };
}



Tabla: Iterable vs Iterator

Es vital no confundirlos en el examen.

Característica Iterable (java.lang) Iterator (java.util)
Rol El Contenedor (La Colección) El Cursor (El Puntero)
Método Principal iterator() hasNext(), next(), remove()
Uso Permite usar for-each Permite recorrer y borrar manualmente
Analogía La Biblioteca El Bibliotecario que te trae libros

Ejercicios Reflexivos

  1. Mapas: Intenta hacer for (Object o : miHashMap). ¿Funciona? ¿Por qué crees que Map no implementa Iterable por defecto? (Pista: ¿Qué devolvería, la clave o el valor?).
  2. Encapsulación: En el ejemplo de EquipoProyecto, ¿por qué es mejor implementar Iterable que simplemente crear un método public List<Alumno> getLista()? (Pista: Piensa en proteger la lista para que nadie la borre desde fuera con un .clear()).
  3. Arrays: Los arrays nativos (int[] numeros) se pueden recorrer con for-each. ¿Significa esto que implementan la interfaz Iterable? (Investigación: Es un caso especial del lenguaje Java).

Para Saber Más