Una guía para el iterador en Java

1. Introducción

Un iterador es una de las muchas formas en que podemos atravesar una colección y, como toda opción, tiene sus pros y sus contras.

Se introdujo por primera vez en Java 1.2 como reemplazo de Enumeraciones y:

  • introdujo nombres de métodos mejorados
  • hizo posible eliminar elementos de una colección sobre la que estamos iterando
  • no garantiza el orden de iteración

En este tutorial, vamos a revisar la interfaz simple de Iterator para aprender cómo podemos usar sus diferentes métodos.

También comprobaremos la extensión ListIterator más robusta que agrega algunas funciones interesantes.

2. La interfaz del iterador

Para empezar, necesitamos obtener un iterador de una colección ; esto se hace llamando al método iterator () .

Para simplificar, obtendremos la instancia de Iterator de una lista:

List items = ... Iterator iter = items.iterator();

La interfaz de Iterator tiene tres métodos principales:

2.1. hasNext ()

El método hasNext () se puede usar para verificar si queda al menos un elemento por recorrer.

Está diseñado para ser utilizado como una condición en mientras bucles:

while (iter.hasNext()) { // ... }

2.2. siguiente()

El método next () se puede utilizar para pasar sobre el siguiente elemento y obtenerlo:

String next = iter.next();

Es una buena práctica usar hasNext () antes de intentar llamar a next () .

Los iteradores de colecciones no garantizan la iteración en ningún orden en particular, a menos que una implementación particular lo proporcione.

2.3. eliminar()

Finalmente, si queremos eliminar el elemento actual de la colección, podemos usar el comando remove:

iter.remove();

Esta es una forma segura de eliminar elementos mientras se itera sobre una colección sin riesgo de una ConcurrentModificationException.

2.4. Ejemplo de iterador completo

Ahora podemos combinarlos todos y ver cómo usamos los tres métodos juntos para el filtrado de colecciones:

while (iter.hasNext()) { String next = iter.next(); System.out.println(next); if( "TWO".equals(next)) { iter.remove(); } }

Así es como usamos comúnmente un Iterador, verificamos de antemano si hay otro elemento, lo recuperamos y luego realizamos alguna acción sobre él.

2.5. Iterando con expresiones lambda

Como vimos en los ejemplos anteriores, es muy detallado usar un iterador cuando solo queremos repasar todos los elementos y hacer algo con ellos.

Desde Java 8, tenemos el método forEachRemaining que permite el uso de lambdas para procesar los elementos restantes:

iter.forEachRemaining(System.out::println);

3. La interfaz ListIterator

ListIterator es una extensión que agrega una nueva funcionalidad para iterar sobre listas:

ListIterator listIterator = items.listIterator(items.size());

Observe cómo podemos proporcionar una posición inicial que en este caso es el final de la Lista.

3.1. hasPrevious () y previous ()

ListIterator se puede usar para el recorrido hacia atrás, por lo que proporciona equivalentes de hasNext () y next () :

while(listIterator.hasPrevious()) { String previous = listIterator.previous(); }

3.2. nextIndex () y previousIndex ()

Además, podemos atravesar índices y no elementos reales:

String nextWithIndex = items.get(listIterator.nextIndex()); String previousWithIndex = items.get(listIterator.previousIndex());

Esto podría resultar muy útil en caso de que necesitemos conocer los índices de los objetos que estamos modificando actualmente, o si queremos mantener un registro de los elementos eliminados.

3.3. añadir()

El método add , que, como su nombre indica, nos permite añadir un elemento antes del elemento que devolvería next () y después del devuelto por previous ():

listIterator.add("FOUR");

3.4. conjunto()

El último método que vale la pena mencionar es set (), que nos permite reemplazar el elemento que se devolvió en la llamada a next () o previous () :

String next = listIterator.next(); if( "ONE".equals(next)) { listIterator.set("SWAPPED"); }

Es importante tener en cuenta que esto solo se puede ejecutar si no se realizaron llamadas previas a add () o remove () .

3.5. Ejemplo completo de ListIterator

Ahora podemos combinarlos todos para hacer un ejemplo completo:

ListIterator listIterator = items.listIterator(); while(listIterator.hasNext()) { String nextWithIndex = items.get(listIterator.nextIndex()); String next = listIterator.next(); if("REPLACE ME".equals(next)) { listIterator.set("REPLACED"); } } listIterator.add("NEW"); while(listIterator.hasPrevious()) { String previousWithIndex = items.get(listIterator.previousIndex()); String previous = listIterator.previous(); System.out.println(previous); }

En este ejemplo, comenzamos obteniendo el ListIterator de la List , luego podemos obtener el siguiente elemento por índice, que no aumenta el elemento actual interno del iterador, o llamando a next .

Luego, podemos reemplazar un elemento específico con set e insertar uno nuevo con add.

Después de llegar al final de la iteración, podemos retroceder para modificar elementos adicionales o simplemente imprimirlos de abajo hacia arriba.

4. Conclusión

La interfaz de Iterator nos permite modificar una colección mientras la atravesamos, lo cual es más difícil con una simple instrucción for / while. Esto, a su vez, nos da un buen patrón que podemos usar en muchos métodos que solo requieren procesamiento de colecciones manteniendo una buena cohesión y bajo acoplamiento.

Finalmente, como siempre, el código fuente completo está disponible en GitHub.