"¡Hola, amigo!"

"¡Hola, Ellie!"

"Hoy quiero hablarles sobre los iteradores".

"Los iteradores se inventaron prácticamente al mismo tiempo que las colecciones. El propósito principal de las colecciones es almacenar elementos, y el propósito principal de un iterador es recuperar estos elementos uno por uno".

"¿Qué tiene de difícil conseguir un conjunto de elementos?"

"Primero, los elementos de algunas colecciones, como Set, no tienen un orden establecido y/o el orden cambia constantemente".

"En segundo lugar, algunas estructuras de datos pueden almacenar objetos de una manera muy compleja: en diferentes grupos, listas, etc. En otras palabras, distribuir todos los elementos en orden sería una tarea nada trivial".

"Tercero, las colecciones tienden a cambiar. Suponga que decide mostrar todo el contenido de una colección, pero justo en el medio de la salida, la JVM cambia a otro subproceso que reemplaza la mitad de los elementos de la colección. Entonces, en lugar de la salida, obtiene quién sabe qué".

"Mmm..."

"¡Pero! Estos son precisamente el tipo de problemas que un iterador puede resolver. Un iterador es un objeto especial dentro de una colección que, por un lado, tiene acceso a todos sus datos privados y conoce su estructura interna, y por otro lado , implementa la interfaz pública Iterator, que permite que todos sepan cómo trabajar con ella " .

"Algunos iteradores tienen una matriz interna en la que se copian todos los elementos de la colección cuando se crea el iterador. Esto garantiza que cualquier cambio posterior en la colección no afectará el número o el orden de los elementos".

"Creo que te has encontrado con esto cuando trabajas con for each . No puedes recorrer una colección y eliminar elementos de ella simultáneamente. Todo esto se debe precisamente a la forma en que funciona un iterador".

"En las nuevas colecciones agregadas a la biblioteca de concurrencia, el iterador se reelaboró ​​para eliminar este problema".

"Déjame recordarte cómo funciona un iterador".

"Java tiene una interfaz Iterator especial. Estos son sus métodos:"

Métodos de la interfaz Iterator<E> Descripción
boolean hasNext() Comprueba si hay más elementos.
E next() Devuelve el elemento actual y pasa al siguiente.
void remove() Elimina el elemento actual

"Un iterador le permite obtener sucesivamente todos los elementos de una colección. Es más lógico pensar en un iterador como algo así como un InputStream: tiene todos los datos, pero su tarea es generarlos secuencialmente".

"El   método next () devuelve el siguiente elemento de la colección".

"El método hasNext () se usa para verificar si hay más elementos".

"Y eliminar () elimina el elemento actual".

"¿Alguna pregunta?"

"¿Por qué los métodos tienen nombres tan extraños? ¿Por qué no isEmpty() y getNextElement()?"

"¿No tendría eso más sentido?"

"Tendría más sentido, pero los nombres provienen del lenguaje C++, donde los iteradores aparecieron antes".

"Ya veo. Continuemos".

"Además de un iterador, también existe la interfaz Iterable, que debe ser implementada por todas las colecciones que admiten iteradores. Tiene un método único:"

Métodos de la interfaz Iterable<T> Descripción
Iterator<T>iterator() Devuelve un objeto iterador.

"Puede usar este método en cualquier colección para hacer que un objeto iterador recorra sus elementos. Recorramos todos los elementos de un TreeSet :"

Ejemplo
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

"Usar un iterador como este no es muy conveniente: hay demasiado código superfluo y obvio. La situación se hizo más simple cuando apareció el ciclo for-each en Java".

"Ahora este código es mucho más compacto y legible:"

Antes Después
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

"¡Este es el mismo código! El iterador se usa en ambos casos".

"Es solo que su uso está oculto en el bucle for-each . Tenga en cuenta que el código de la derecha no tiene ningún texto rojo . El uso del iterador está completamente oculto".

"Se puede usar un bucle for-each para cualquier objeto que admita iteradores. En otras palabras, puede escribir su propia clase, agregarle el método iterator () y usar sus objetos en una construcción for-each ".

"¡Guau! Por supuesto, no estoy ansioso por escribir mis propias colecciones e iteradores, pero la perspectiva sigue siendo tentadora. Lo anotaré".

Además, hay otro tipo popular de iterador que incluso tiene su propia interfaz. Estoy hablando de un iterador para listas, es decir, ListIterator .

"Independientemente de su implementación, las listas mantienen el orden de los elementos, lo que hace que trabajar con ellos a través de un iterador sea un poco más conveniente".

"Estos son los métodos de la interfaz ListIterator <E>:"

Método Descripción
boolean hasNext() Comprueba si hay más elementos por delante.
E next() Devuelve el siguiente elemento.
int nextIndex() Devuelve el índice del siguiente elemento.
void set(E e) Cambia el valor del elemento actual
boolean hasPrevious() Comprueba si hay algún elemento detrás.
E previous() Devuelve el elemento anterior
int previousIndex() Devuelve el índice del elemento anterior.
void remove() Elimina el elemento actual
void add(E e) Agrega un elemento al final de la lista.

"En otras palabras, aquí podemos avanzar y retroceder. Y hay un par de otras características pequeñas".

"Bueno, eso es algo interesante. ¿Dónde se usa?"

"Supongamos que desea avanzar y retroceder en una lista enlazada. La operación de obtención será bastante lenta, pero la operación de siguiente () será muy rápida".

"Hmm. Me convenciste. Lo tendré en cuenta".

"¡Gracias, Ellie!"