> For the complete documentation index, see [llms.txt](https://educacion.gitbook.io/programacion/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://educacion.gitbook.io/programacion/ud6-estructuras-de-datos-avanzadas/estructuras-de-datos-en-java/interfaces-en-colleccions/iterator-e-iterable.md).

# Iterator e Iterable

**Iterable** e **Iterator** son dúas interfaces que establecen un contrato entre clases:

* **Iterable** e unha interface que garantiza que a clase que a implementa é percorrible&#x20;
* **Iterator** é unha interface que asegura que unha clase poda percorrer unha clase iterable

{% hint style="warning" %}
Esta práctica afonda no **principio SOLID de responsabilidade única**, onde unha clase ten a responsabilidade de almacenar colecciones, e outra ten a responsabilidade de saber recorrerla.
{% endhint %}

## Iterable\<T>

A interface Iterable\<T> en Java é unha interface tipada fundamental que **permite que os obxectos dunha clase sexan percorridos ou iterados de maneira secuencial**. Practicamente, todas as coleccións en Java (como listas, conxuntos, mapas, etc.) implantan esta interface, o que facilita a súa iteración mediante un *for-each loop* ou un *iterator* explícito.

{% hint style="warning" %}
**Documentación oficial:** <https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Iterable.html>&#x20;
{% endhint %}

A interface `Iterable<T>`<i class="fa-copy">:copy:</i> define un único método importante:

```java
public interface Iterable<T> {
    Iterator<T> iterator();
}
```

A interface `Iterable`<i class="fa-copy">:copy:</i> é xenérica, o que significa que pode ser usada para calquera tipo de obxecto. O tipo `T` representa o tipo de elemento que se almacenará na colección. O método `iterator()`<i class="fa-copy">:copy:</i> devolve un obxecto de tipo `Iterator<T>`<i class="fa-copy">:copy:</i>, que é un **iterador que permite percorrer os elementos da colección de maneira secuencial.**

{% hint style="warning" %}
Iterable e unha interface que xerarquicamente está por enrriba de Collection, co que **todas as interfaces que herden de Collection van a contar con un método iterator()**
{% endhint %}

```mermaid
classDiagram
    class Iterable~T~ {
        <<interface>>
        +iterator() Iterator~T~
        +forEach(Consumer~? super T~ action)
        +spliterator() Spliterator~T~
    }
    
    class Collection~T~ {
        <<interface>>
        +size() int
        +isEmpty() boolean
        +contains(Object o) boolean
        +add(T e) boolean
        +remove(Object o) boolean
        +clear() void
    }
    
    class List~T~ {
        <<interface>>
        +get(int index) T
        +set(int index, T element) T
        +add(int index, T element) void
        +remove(int index) T
        +indexOf(Object o) int
    }
    
    class Set~T~ {
        <<interface>>
        +add(T e) boolean
    }
    
    class Queue~T~ {
        <<interface>>
        +offer(T e) boolean
        +poll() T
        +peek() T
    }
    
    class Deque~T~ {
        <<interface>>
        +addFirst(T e) void
        +addLast(T e) void
        +removeFirst() T
        +removeLast() T
    }
    
    Iterable~T~ <|-- Collection~T~
    Collection~T~ <|-- List~T~
    Collection~T~ <|-- Set~T~
    Collection~T~ <|-- Queue~T~
    Queue~T~ <|-- Deque~T~
```

A interface `Iterable`<i class="fa-copy">:copy:</i> proporciona a funcionalidade básica de iteración. Un obxecto que implanta `Iterable`<i class="fa-copy">:copy:</i> pode ser percorrido utilizando un `Iterator`<i class="fa-copy">:copy:</i>, ou se é unha colección de Java, pode ser percorrida facilmente utilizando un *for-each loop*. O código de iteración mediante iterator ou for-each sería o seguinte:

```java
Iterator<String> it = lista.iterator();
while (it.hasNext()){
    System.out.println(it.next());
}
```

```java
// Usando o for-each loop (método simplificado de iterar sobre os elementos)
for (String item : iterable) {
    System.out.println(item);
}
```

Internamente, o `for-each loop`<i class="fa-copy">:copy:</i> chama ao método `iterator()`<i class="fa-copy">:copy:</i> da interface `Iterable`<i class="fa-copy">:copy:</i> para obter un `Iterator`<i class="fa-copy">:copy:</i> e logo usa ese `Iterator`<i class="fa-copy">:copy:</i> para percorrer os elementos.

## Iterator\<E>

A interface `Iterator<E>`<i class="fa-copy">:copy:</i> define os seguintes métodos:

* **`hasNext()`**<i class="fa-copy">:copy:</i>: Retorna `true` se hai máis elementos na colección que se poden percorrer, e `false` se non.
* **`next()`**<i class="fa-copy">:copy:</i>: Retorna o seguinte elemento na colección e avanza o iterador ao seguinte elemento.
* **`remove()`**<i class="fa-copy">:copy:</i>: Elimina o último elemento retornado polo `next()`<i class="fa-copy">:copy:</i>. Este método non está implantado en todos os iteradores, pero pode ser usado se a implantación o permite.

A interface `Iterable`<i class="fa-copy">:copy:</i> fai que calquera obxecto que a implante sexa compatible co *for-each loop*, proporcionando unha maneira estándar de percorrer elementos dunha colección como por exemplo a clase `ArrayList`<i class="fa-copy">:copy:</i>. Tamén facilita a compatibilidade con outras APIs e estruturas de datos que utilizan `Iterator`<i class="fa-copy">:copy:</i> para percorrer obxectos.

{% hint style="warning" %}
**Documentación oficial:** <https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Iterator.html>
{% endhint %}

## Borrados: for-each vs iterator

Cando empregamos un bucle for-each en Java, o compilador tradúceo internamente nun iterador, que queda oculto para o programador. Neste punto:

* A **colección** traballa con un contador interno **modCount** que conten o numero de modificaciones.
* O **iterador** traballa con un contador interno **expectedModCount = modCount**
* As modificacións no contador do iterador, SINCRONIZAN as modificacións do contador da colección
* As modificacións no contador da coleccion, NON SINCRONIZAN as modificacións da colección

&#x20;Ao chamar a `coleccion.remove(elemento)` dentro do bucle, **estamos modificando a colección directamente, por fóra do iterador, de xeito que o seu estado interno queda desincronizado**: o cursor non sabe que un elemento desapareceu, e no seguinte `next()` ou ben salta un elemento sen procesalo ou ben detecta a modificación a través do `modCount` e lanza unha excepción.

{% hint style="warning" %}
**Para borrados nunha colección, recomendase empregar un iterador** para evitar problemas de desincronización. Unha alterantiva sería traballar con índices naquelas coleccións que os adminten (P.e: Listas), pero lembra que será necesario axustar o indice tras cada borrado
{% endhint %}

<figure><img src="/files/odZdcBFtLoSWkTZSbCCu" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**ConcurrentModificationException** e lanzada por clases coma **ArrayList, LinkedList, HashSet, TreeSet,** ...

**Non é lanzada por ArrayDeque**
{% endhint %}

```java
import java.util.List;

public class EjemploFallo {

    public static void main(String[] args) {
        List<String> lista = new ArrayList<>();
        lista.add("A Coruña");
        lista.add("Lugo");
        lista.add("Santiago");

        System.out.println("Lista: " + lista);

        System.out.println("\nIntentando imprimir con for-each + remove()...");
        try {
            for (String doc : lista) {
                lista.remove(doc);           
                System.out.println(doc);
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("ConcurrentModificationException: " + e);
            System.out.println(" Lista tras excepcion: " + lista);
        }

        lista.clear();
        lista.add("A Coruña");
        lista.add("Lugo");
        lista.add("Santiago");
        
        Iterator<String> it = lista.iterator();
        while (it.hasNext()) {
            String doc = it.next();
            it.remove();                     // sincronizado con el iterator
            System.out.println(doc);
        }
        System.out.println("Lista : " + lista);
    }
}
```

## Exemplo de implementacion: A clase Lista

Para implementar un Iterador na clase Lista que implementamos no apartado de listas de esta unidade, o primerio que se vai a facer sería crear unha clase privada que implemente Iterator. A clase privada implementase dentro do mesmo ficheiro que a clase Lista, e o seu obxectivo e ter acceso as estructuras de datos da lista para poder realizar a iteración:&#x20;

```java
private class IteradorDeListas implements Iterator<E>{

       private Nodo<E> actual;

       public IteradorDeListas(){
           this.actual = cabecera;
       }

        @Override
        public boolean hasNext() {
           //next devolve se o elemento actual existe. Pensar o exercicio desde iteracion 0
            return this.actual!=null;
        }

        @Override
        public E next() {
            //Devuelve el elemento actual y pasa al siguiente
            E elemento = this.actual.getElemento();
            this.actual = actual.getSiguiente();
            return elemento;
        }
    }
```

```java
public class Lista<E> implements Iterable<E>{

    private Nodo<E> cabecera;
    private int tamano;

    public Lista(){
        this.cabecera = null;
        this.tamano = 0;
    }

    public E get(int posicion){
        if (posicion < 0 || posicion > this.tamano){
            throw new IndexOutOfBoundsException("Fora de rango");
        }
        Nodo res = this.cabecera;
        for (int i = 0; i<posicion; i++)
            res = res.getSiguiente();
        return (E) res.getElemento();
    }

    public boolean add(E elemento, int posicion){
        if (posicion < 0 || posicion > this.tamano){
            return false;
        }
        Nodo<E> nuevo = new Nodo(elemento);
        if (posicion==0){
            nuevo.setSiguiente(this.cabecera);
            this.cabecera = nuevo;
        } else {
            Nodo<E> anterior = (Nodo) this.cabecera;
            for (int i = 0; i<posicion-1; i++)
                anterior = anterior.getSiguiente();
            Nodo<E> siguiente = (Nodo) anterior.getSiguiente();
            nuevo.setSiguiente(siguiente);
            anterior.setSiguiente(nuevo);
        }
        this.tamano++;
        return true;
    }

    public boolean eliminar(E elemento){
        if (this.tamano==0)
            return false;
        if (this.cabecera.equals(elemento)){
            this.cabecera = this.cabecera.getSiguiente();
            this.tamano--;
            return true;
        }
        Nodo<E> actual = (Nodo) this.cabecera;
        while (actual.getSiguiente()!=null){
            if(actual.getSiguiente().getElemento().equals(elemento)){
                actual.setSiguiente(actual.getSiguiente().getSiguiente());
                this.tamano--;
                return true;
            }
            actual = (Nodo) actual.getSiguiente();
        }
        return false;
    }

    @Override
    public String toString() {
        if (this.tamano==0)
            return "Lista[]";
        String res = "Lista[";
        Nodo<E> actual = (Nodo) this.cabecera;
        for (int i = 0; i<this.tamano; i++){
            res += actual.getElemento();
            if (actual.getSiguiente()!=null)
                res+=",";
            actual = actual.getSiguiente();
        }
        res+="]";
        return res;
    }

    @Override
    public Iterator<E> iterator() {
        //Unicamente é necesario implementar este método
        return new IteradorDeListas();
    }

    @Override
    public void forEach(Consumer action) {
        Iterable.super.forEach(action);
    }

    @Override
    public Spliterator spliterator() {
        return Iterable.super.spliterator();
    }
```

Unha vez implementado isto, na clase **App** imos poder empregar os métodos de iteración de for-each e iterator:

```java
Lista<String> cidades = new Lista<>();

        System.out.println("--- Engadindo cidades ---");
        cidades.add("A Coruña", 0);
        cidades.add("Vigo", 1);
        cidades.add("Lugo", 1); // Debería quedar no medio
        cidades.add("Ourense", 0); // Debería ser a nova cabeza

        Iterator<String> it = cidades.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        //Se non definimos iterator, este bucle non funciona
        for(String s: cidades){
            System.out.println(s);
        }
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://educacion.gitbook.io/programacion/ud6-estructuras-de-datos-avanzadas/estructuras-de-datos-en-java/interfaces-en-colleccions/iterator-e-iterable.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
