Освой Kotlin играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Sequence

Создание последовательности
Списки против последовательности

Создание последовательности

Sequence (последовательность) очень похожа на списки List. По сути они являются аналогами потоков Stream в Java 8. Но у последовательности есть очень полезные возможности для эффективных операций по сравнению со списками.

Создать последовательность можно через функцию sequenceOf()


val cats = sequenceOf("Барсик", "Мурзик", "Рыжик", "Васька")

Если у вас есть уже готовые списки List или множества Set, то их можно преобразовать в последовательность через asSequence().


val cats = listOf("Барсик", "Мурзик", "Рыжик", "Васька")
val catsSequence = cats.asSequence()

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



val numbers = generateSequence(1) { it + 3 }
// бесконечная последовательность 1, 4, 7, 10, 13 и т.д.
println(numbers.count())

В примере берётся первый элемент, к нему прибавляется значение 3 для создания второго элемента, затем ко второму элементу снова прибавляется 3 для создания третьего элемента и так до бесконечности. Этот пример вызовет ошибку.

Вам нужно добавить условие, чтобы у последнего элемента последовательности значение равнялось бы null.


val numbers = generateSequence(1) { if (it < 15) it + 3 else null}
println(numbers.count())
println(numbers.toList())

System.out: 6 System.out: [1, 4, 7, 10, 13, 16]

Последний способ создания последовательности - функция sequence(). Пример из документации.


val oddNumbers = sequence {
    yield(1)
    yieldAll(listOf(3, 5))
    yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList()) // [1, 3, 5, 7, 9]

Списки против последовательности

Сравним работу со списком и последовательностью для выполнения одной задачи - имеется длинная строка, которую нужно разбить на отдельные слова, затем выбрать из них слова, чья длина превышает три символа и вывести длину каждого слова из первых четырёх выбранных слов на экран.


val poems = "А у нас сегодня кошка родила вчера котят".split(" ")
val lengthsList = poems
    .filter { println("filter: $it"); it.length > 3 }
    .map { println("длина: ${it.length}"); it.length }
    .take(4)

println("Длина первых четырёх слов превышает три символа:")
println(lengthsList)

filter: А filter: у filter: нас filter: сегодня filter: кошка filter: родила filter: вчера filter: котят длина: 7 длина: 5 длина: 6 длина: 5 длина: 5 Длина первых четырёх слов превышает три символа: [7, 5, 6, 5]

Если запустить код, то увидим, что filter() и map() работают с каждым элементом списка. Визуально код работает следующим образом.

List

Теперь решим задачу через последовательность.


val poems = "А у нас сегодня кошка родила вчера котят".split(" ")
// Конвертируем список в последовательность
val poemSequence = poems.asSequence()

val lengthsSequence = poemSequence
    .filter { println("filter: $it"); it.length > 3 }
    .map { println("длина: ${it.length}"); it.length }
    .take(4)

println("Длина первых четырёх слов превышает три символа:")
println(lengthsSequence.toList())

Длина первых четырёх слов превышает три символа: filter: А filter: у filter: нас filter: сегодня длина: 7 filter: кошка длина: 5 filter: родила длина: 6 filter: вчера длина: 5 [7, 5, 6, 5]

Обратите внимание на разницу. Сначала выводится строка, которая в предыдущем примере выводилась предпоследней. Затем вызывается filter() для каждого слова. Если слово меньше трёх символов, оно пропускается. Если слово больше трёх символов, то вызывается map(). Когда наберётся четыре слова по условию take(4), то дальнейшие операции прекращаются.

Визуально выглядит следующим образом.

Sequence

Для решения задачи понадобилось 19 шагов вместо 22. Для несложного примера экономия невелика, но в больших коллекциях разница становится ощутимой.

В списках нам приходится проходить по всем элементам и применять операции фильтрации и прочих функций. В последовательностях нам не нужно проходить по всем элементам, мы сразу применяем все нужные операции к элементу в последовательном режиме и прекращаем операции, если цель достигнута. Соответственно, нужно грамотно создавать цепочки операций, чтобы экономить ресурсы.

Реклама