Итераторы

Для обхода элементов коллекции стандартная библиотека Kotlin поддерживает механизм итераторов - объектов, которые предоставляют доступ к элементам последовательно, не раскрывая базовую структуру коллекции. Итераторы полезны, когда вам нужно обработать все элементы коллекции один за другим, например, вывести в лог их значения или обновить.

Итераторы доступны всем наследникам интерфейса Iterable<T>, включая Set и List, путём вызова функции iterator().

При получении итератора он сначала указывает на первый элемент коллекции; вызов функции next() возвращает этот элемент и перемещает позицию итератора на следующий элемент, если такой существует.

Как только итератор проходит через последний элемент, его больше нельзя использовать для извлечения элементов; его также нельзя вернуть в предыдущее положение. Чтобы снова перебрать коллекцию, нужно создать новый итератор.

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    val numbersIterator = numbers.iterator()
    while (numbersIterator.hasNext()) {
        println(numbersIterator.next())
    }
}

// В логе будет:
// one
// two
// three
// four

Другой способ перебрать Iterable коллекцию - это всем известный цикл for. При использовании for вы неявно получаете итератор. Поэтому, приведённый ниже код эквивалентен предыдущему примеру:

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    for (item in numbers) {
        println(item)
    }
}

// В логе будет:
// one
// two
// three
// four

Также, есть полезная функция forEach(), которая позволяет автоматически перебирать коллекцию и выполнять заданный код для каждого элемента. Перепишем пример выше:

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    numbers.forEach {
        println(it)
    }
}

// В логе будет:
// one
// two
// three
// four

Итераторы списков

Для списков существует специальная реализация итератора: ListIterator. Он поддерживает итерацию списков в обоих направлениях: вперёд и назад.

Обратная итерация реализуется функциями hasPrevious() и previous(). Кроме того, ListIterator предоставляет информацию об индексах элементов с помощью функций nextIndex() и previousIndex().

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    val listIterator = numbers.listIterator()
    while (listIterator.hasNext()) listIterator.next()
    println("Iterating backwards:")
    while (listIterator.hasPrevious()) {
        print("Index: ${listIterator.previousIndex()}")
        println(", value: ${listIterator.previous()}")
    }
}

// В логе будет:
// Iterating backwards:
// Index: 3, value: four
// Index: 2, value: three
// Index: 1, value: two
// Index: 0, value: one

Наличие возможности итерации в обоих направлениях означает, что ListIterator может быть использован даже после того, как он достигнет последнего элемента.

Итераторы изменяемых коллекций

Для итерации изменяемых коллекций существует MutableIterator, который расширяет Iterator с помощью функции удаления элементов remove(). Поэтому, вы можете удалять элементы из коллекции во время итерации.

fun main() {
    val numbers = mutableListOf("one", "two", "three", "four")
    val mutableIterator = numbers.iterator()

    mutableIterator.next()
    mutableIterator.remove()    
    println("After removal: $numbers") // [two, three, four]
}

Помимо удаления элементов, MutableListIterator позволяет добавлять и заменять элементы во время итерации.

fun main() {
    val numbers = mutableListOf("one", "four", "four")
    val mutableListIterator = numbers.listIterator()

    mutableListIterator.next()
    mutableListIterator.add("two")
    mutableListIterator.next()
    mutableListIterator.set("three")   
    println(numbers) // [one, two, three, four]
}