Recorriendo colecciones

Iterando sobre colecciones en Kotlin

Para recorrer los elementos de una colección, la biblioteca estándar de Kotlin admite el mecanismo de iteración de la interface Iterator que proporciona acceso a los elementos de forma secuencial, lo que resulta útil cuando se necesita procesar, uno por uno, todos los elementos para, por ejemplo, obtener sus valores u operar con cada uno de ellos.

En general un iterador se aplica sobre una colección, u otro objeto que se pueda representar como una secuencia de elementos, para acceder de forma secuencial a sus elementos.

Llamando a la función iterator() se pueden obtener iteradores en todos los herederos de la interface Iterable, entre los que se incluyen List y Set. Por tanto, todas las clases que heredan de esta interfaz pueden representarse como una secuencia de elementos sobre los que se puede iterar.

Una vez que se obtiene un iterador, éste apunta al primer elemento de la colección y la función next() devuelve este elemento y mueve la posición del iterador al siguiente elemento si existe, lo que podemos comprobar con hasNext(). Cuando llega al último elemento, el iterador no puede retroceder a ninguna posición anterior ni recuperar elementos previos, por lo que para recorrer nuevamente la colección debemos crear un nuevo iterador.
fun main() {
    // List<String>
    val numeros = listOf("uno", "dos", "tres", "cuatro")
    // Iterator<String>
    val numerosIterador = numeros.iterator()
    while (numerosIterador.hasNext()) { // devuelve true si el iterador tiene más elementos
        println(numerosIterador.next()) // mueve el iterador al siguiente elemento
    }
}

Otra forma de recorrer una colección Iterable es con el bucle for, obteniéndose el iterador implícitamente. Entonces, el siguiente código es equivalente al ejemplo anterior:
fun main() {
    val numeros = listOf("uno", "dos", "tres", "cuatro")
    for (item in numeros) {
        println(item)
    }
}

Pero para realizar una acción determinada sobre cada elemento suele ser más práctico utilizar la función forEach() que permite iterar automáticamente sobre una colección a la vez que ejecuta un código dado sobre cada elemento. Aplicando forEach al ejemplo anterior se vería así:
fun main() {
    val numeros = listOf("uno", "dos", "tres", "cuatro")
    numeros.forEach {
        println(it)
    }
}

Para las listas hay una implementación especial de iterador: ListIterator, que soporta recorrer las listas en ambas direcciones: hacia adelante y hacia atrás. La iteración hacia atrás se implementa mediante las funciones hasPrevious() y previous(). Además, ListIterator proporciona información sobre los índices del elemento con las funciones nextIndex() y previousIndex().
fun main() {
    val numeros = listOf("uno", "dos", "tres", "cuatro")
    val listIterator = numeros.listIterator()
    while (listIterator.hasNext()) listIterator.next()  // llega al final de la lista
    println("Hacia atrás:")
    while (listIterator.hasPrevious()) {
        print("Índice: ${listIterator.previousIndex()}")
        println(", valor: ${listIterator.previous()}")
    }
}
Hacia atrás:
Índice: 3, valor: cuatro
Índice: 2, valor: tres
Índice: 1, valor: dos
Índice: 0, valor: uno

Como vemos, tener la capacidad de iterar en ambas direcciones significa que ListIterator todavía se puede usar después de alcanzar el último elemento.

Para el caso de colecciones mutables, la interface MutableIterator proporciona la capacidad de eliminar elementos mientras los recorre con la función remove(), y la interface MutableListIterator permite añadir y remplazar elementos con las funciones add() y set():
// MutableList<String>
val numeros = mutableListOf("uno", "dos", "tres", "cuatro")

// MutableIterator<String>
val mutableIterador = numeros.iterator()
mutableIterador.next()
mutableIterador.remove()
println(numeros) // [dos, tres, cuatro]

// MutableListIterator<String>
val mutableListIterador = numeros.listIterator()
mutableListIterador.add("uno")
println(numeros) // [uno, dos, tres, cuatro]

mutableListIterador.next()
mutableListIterador.set("2")
println(numeros) // [uno, 2, tres, cuatro]

Otro ejemplo de MutableListIterator:
import kotlin.math.absoluteValue

fun main() {
    // ArrayList<Int> (lista mutable)
    val numLista = arrayListOf(1, -20, 10, -55, 30, -22, -11, 0, 99)

    // MutableListIterator<Int>
    val iterador = numLista.listIterator()
    while (iterador.hasNext()) {
        val valor = iterador.next()
        if (valor < 0) iterador.set(valor.absoluteValue)
    }
    println(numLista) // [1, 20, 10, 55, 30, 22, 11, 0, 99]
}

Comentarios

Entradas populares

I/O: entrada y salida de datos en consola

Recursos gratis para aprender Kotlin

Lectura y escritura de archivos