Наряду с коллекциями Kotlin предоставляет еще один тип наборов элементов – последовательности (sequences). Последовательности предоставляют похожую функциональность, что и интерфейс Iterable, который реализуется типами коллекций. Ключевая разница состоит в том, как обрабатываются элементы последовательности при применении к ним набора операций.
Последовательности представляют интерфейс Sequence<T>. Для создания объекта данного типа можно использовать встроенную функцию sequenceOf(). В качестве параметра она принимает набор элементов, которые будут входить в последовательность. Например:
val people = sequenceOf("Tom", "Sam", "Bob") //тип Sequence<String> println(people.joinToString()) // Tom, Sam, Bob
В данном случае определяется последовательность типа Sequence<String>
, которая содержит три элемента.
Для вывода последовательности на консоль применяется ее преобразование в строку с помощью функции joinToString()
Также можно создать последовательность из объекта Iterable (например, из объектов типа List или Set), используя метод asSequence()
val employees = listOf("Tom", "Sam", "Bob") // объект List<String> val people = employees.asSequence() //тип Sequence<String> println(people.joinToString()) // Tom, Sam, Bob
Третий способ создания последовательности представляет функция generateSequence
var number = 0 val numbers = generateSequence{ number += 2; number} println(numbers.take(5).joinToString()) // получаем первые 5 элементов последовательности - 2, 4, 6, 8, 10
В качестве параметра функция generateSequence()
принимает функцию, которая возвращает некоторое значение.
Это значение затем станет элементом последовательности. Так в данном случае увеличиваем значение переменной number на 2 и возвращаем ее значение.
То последовательность будет содержать числа с шагом в 2: 2, 4, 6, 8, 10, 12....
Стоит учитывать, что эта функция по умолчанию генерирует бесконечную последовательность.
Поэтому в примере выше при выводе элементов на консоль с помощью метода take()
получаем только первые пять элементов. Чтобы ограничить
генерируемую последовательность, функция в generateSequence()
должна возвращать null:
var number = 0 val numbers = generateSequence{ number += 2; if(number > 8) null else number} println(numbers.joinToString()) // 2, 4, 6, 8
В данном случае последовательность содержит только числа от 2 до 8 включительно.
Функция generateSequence()
имеет несколько вариаций. В частности, в качестве первого параметра мы можем
передать первое значение по умолчанию, а второй параметр по прежнему представляет функцию, которая генерирует последовательность
val numbers = generateSequence(5){ if(it == 25) null else it + 5} println(numbers.joinToString()) // 5, 10, 15, 20, 25
В данном случае в функцию первому параметру передается число 5, то есть это будет первый элемент последовательности.
Функция создания последовательности через параметр it получает предыдущий результат функции - фактически предыдущий элемент последовательности
(при первом вызове функции - это значение первого параметра).
В данном случае создается конечная последовательность, где каждый последующий элемент больше предыдущего на 5. Если
последний элемент равен 25, то возвращаем null, тем самым завершаем генерацию последовательности. Стоит отметить,
что даже если функция возвратит null, то последовательность будет содержать один элемент: generateSequence(5){ null }
Четвертый способ представляет применение функции sequence(). В этой функции можно генерировать элементы последовательности с помощью функций yield() и yieldAll():
val numbers = sequence { yield(1) yield(4) yield(7) } println(numbers.joinToString()) // 1, 4, 7
Функция yield() фактически возвращает во вне некоторое значение, которое ей передается через параметр.
То есть при первом обращении к функции sequence сработает вызов yield(1)
, который возвратит значение 1. При втором обращении
сработает вызов yield(4)
, который возвратит 4. И при третьем обращении сработает вызов yield(7)
.
Таким образом, последовательность будет содержать 3 элемента: 1, 4, 7.
Можно создать и бесконечную последовательность:
val numbers = sequence { var start = 0 while(true) yield(start++) } println(numbers.take(5).joinToString()) // 0, 1, 2, 3, 4
Здесь при каждом обращении к функции sequence возвращается одно из чисел начиная с 0.
Если надо создать последовательности на основе другой последовательности или коллекции, то удобнее источник данных передать в функцию yieldAll()
val personal = sequence { val data = listOf("Alice", "Kate", "Ann") yieldAll(data) } println(personal.joinToString()) // Alice, Kate, Ann
Для перебора последовательности можно применять стандартный цикл for:
val people = sequenceOf("Tom", "Sam", "Bob") for(person in people) println(person)
Интерфейс Sequence предоставляет ряд функций, который позволяют производить различные операции с последовательностями. Все операции с последовательностями делятся на ряд типов.
Прежде всего операции различаются по наличию состояния. Одни операции могут хранить состояние (statefull-операции), например, операция distinct()
.
Подобное состояние обычно пропорционально количеству элементов в последовательности. А есть операции, которые не имеют состояния (stateless-операции), например,
функции map()
и filter()
, или требуют очень небольшого константного состояния, как функции take()
и drop()
.
Подобные операции обрабатывают каждый элемент независимо от других.
Другая классификация операций основывается на том, когда выполняется операции. В данном случае они бывают промежуточными (intermediate) - такие операции обычно возвращают другую последовательность. Они НЕ выполняются сразу при их вызове.
А есть терминальные или конечные операции (terminal). Такие операции подразумевают извлечение элементов последовательности, например, когда нам надо получить
количество элементов с помощью функции count()
или элемент по индексу. Такие операции выполняются сразу при их вызове. То же самое можно сказать
о переборе с помощью цикла for, который извлекает элементы из последовательности и соответственно также запускает процесс выполнения всех операций последовательности.
Рассмотрим основные операции:
all(predicate: (T) -> Boolean): Boolean
возвращает true
, если все элементы соответствуют предикату, который передается в функцию в качестве параметра
Терминальная операция
any(): Boolean
возвращает true
, если последовательность содержит хотя бы один элемент
Дополнительная версия возвращает true
, если хотя бы один элемент соответствуют предикату, который передается в функцию в качестве параметра
any(predicate: (T) -> Boolean): Boolean
Терминальная операция
average(): Double
возвращает среднее значение для числовой последовательности типов Byte, Int, Short, Long, Float, Double
chunked(size: Int): Sequence<List<T>>
расщепляет последовательность на последовательность из списков List, параметр size устанавливает максимальное количество элементов в каждом из списков
Дополнительная версия в качестве второго параметра получает функцию преобразования, которая преобразует каждый список в элемент новой последовательности
chunked(size: Int,transform: (List<T>) -> R): Sequence<R>
Промежуточная операция
contains(element: T): Boolean
возвращает true
, если последовательность содержит элемент element
Терминальная операция
count(): Int
возвращает количество элементов в последовательности
Дополнительная версия возвращает количество элементов, которые соответствуют предикату
count(predicate: (T) -> Boolean): Int
Терминальная операция
distinct(): Sequence<T>
возвращает новую последовательность, которая содержит только уникальные элементы
Промежуточная операция
distinctBy(selector: (T) -> K): Sequence<T>
возвращает новую последовательность, которая содержит только уникальные элементы с учетом функции селектора, которая передается в качестве параметра
Промежуточная операция
drop(n: Int): Sequence<T>
возвращает новую последовательность, которая содержит все элементы за исключением первых n элементов
Промежуточная операция
dropWhile(predicate: (T) -> Boolean): Sequence<T>
возвращает новую последовательность, которая содержит все элементы за исключением первых элементов, которые соответствуют предикату
Промежуточная операция
elementAt(index: Int): T
возвращает элемент по индексу index. Если индекс выходит за пределы последовательности, то генерируется исключение типа IndexOutOfBoundsException
Терминальная операция
elementAtOrElse(index: Int, defaultValue: (Int) -> T): T
возвращает элемент по индексу index. Если индекс выходит за пределы последовательности, то возвращается значение, устанавливаемое функцией из параметра defaultValue
Терминальная операция
elementAtOrNull(index: Int): T?
возвращает элемент по индексу index. Если индекс выходит за пределы последовательности, то возвращается null
Терминальная операция
filter(predicate: (T) -> Boolean): Sequence<T>
возвращает новую последовательность из элементов, которые соответствуют предикату
Промежуточная операция
filterNot(predicate: (T) -> Boolean): Sequence<T>
возвращает новую последовательность из элементов, которые НЕ соответствуют предикату
Промежуточная операция
filterNotNull(): Sequence<T>
возвращает новую последовательность из элементов, которые не равны null
Промежуточная операция
find(predicate: (T) -> Boolean): T?
возвращает первый элемент, который соответствует предикату. Если элемент не найден, то возвращается null
Терминальная операция
findLast(predicate: (T) -> Boolean): T?
возвращает последний элемент, который соответствует предикату. Если элемент не найден, то возвращается null
Терминальная операция
first(): T
возвращает первый элемент последовательности
Дополнительная версия возвращает первый элемент, которые соответствует предикату
first(predicate: (T) -> Boolean): T
Если элемент не найден, то генерируется исключение типа NoSuchElementException
Терминальная операция
firstOrNull(): T?
возвращает первый элемент последовательности
Дополнительная версия возвращает первый элемент, которые соответствует предикату
firstOrNull(predicate: (T) -> Boolean): T?
Если элемент не найден, то возвращается null
Терминальная операция
flatMap(transform: (T) -> Sequence<R>): Sequence<R>
преобразует последовательность элементов типа T в последовательность элементов типа R, используя функцию преобразования, которая передается в качестве параметра
Промежуточная операция
fold(initial: R, operation: (acc: R, T) -> R): R
Возвращает значение, которое является результатом действия функции operation над каждым элементом последовательности. Первый параметр функции operation - результат работы функции над предыдущим элементом последовательности (при первом вызове - значение из параметра initial), в второй параметр - текущий элемент последовательности.
Терминальная операция
forEach(action: (T) -> Unit)
Выполняет для каждого элемента последовательности действие action.
Терминальная операция
groupBy(keySelector: (T) -> K): Map<K, List<T>>
Группирует элементы по ключу, который возвращается функцией keySelector. Результат функции карта Map, где ключ - собственно ключ элементов, а значение - список List из элементов, которые соответствуют этому ключу
Дополнительная версия принимает функцию преобразования элементов:
groupBy(keySelector: (T) -> K, valueTransform: (T) -> V): Map<K, List<V>>
Терминальная операция
indexOf(element: T): Int
Возвращает индекс первого вхождения элемента element. Если элемент не найден, возвращается -1
Терминальная операция
indexOfFirst(predicate: (T) -> Boolean): Int
Возвращает индекс первого элемента, который соответствует предикату. Если элемент не найден, возвращается -1
Терминальная операция
indexOfLast(predicate: (T) -> Boolean): Int
Возвращает индекс последнего элемента, который соответствует предикату. Если элемент не найден, возвращается -1
Терминальная операция
joinToString(): String
Генерирует из последовательности строку
Терминальная операция
last(): T
возвращает последний элемент последовательности
Дополнительная версия возвращает последний элемент, которые соответствует предикату
last(predicate: (T) -> Boolean): T
Если элемент не найден, то генерируется исключение типа NoSuchElementException
Терминальная операция
lastOrNull(): T?
возвращает последний элемент последовательности
Дополнительная версия возвращает последний элемент, которые соответствует предикату
lastOrNull(predicate: (T) -> Boolean): T?
Если элемент не найден, то возвращается null
Терминальная операция
lastIndexOf(element: T): Int
Возвращает последний индекс элемента element. Если элемент не найден, возвращается -1
Терминальная операция
map(transform: (T) -> R): Sequence<R>
Применяет к элементам последовательности функцию трансформации и возвращает новую последовательность из новых элементов
Промежуточная операция
mapIndexed(transform: (index: Int, T) -> R): Sequence<R>
Применяет к элементам последовательности и их индексам функцию трансформации и возвращает новую последовательность из новых элементов
Промежуточная операция
mapNotNull(transform: (T) -> R?): Sequence<R>
Применяет к элементам последовательности функцию трансформации и возвращает новую последовательность из новых элементов, которые не равны null
Промежуточная операция
maxOf(selector: (T) -> Double): Double
Возвращает максимальное значение на основе селектора
Терминальная операция
maxOfOrNull(selector: (T) -> Double): Double?
Возвращает максимальное значение на основе селектора. Если последовательность пуста, возвращается null
Терминальная операция
maxOrNull(): Double?
Возвращает максимальное значение. Если последовательность пуста, возвращается null
Терминальная операция
minOf(selector: (T) -> Double): Double
Возвращает минимальное значение на основе селектора
Терминальная операция
minOfOrNull(selector: (T) -> Double): Double?
Возвращает минимальное значение на основе селектора. Если последовательность пуста, возвращается null
Терминальная операция
minOrNull(): Double?
Возвращает минимальное значение. Если последовательность пуста, возвращается null
Терминальная операция
minus(element: T): Sequence<T>
Возвращает новую последовательность, которая содержит все элементы текущей за исключением элемента element.
Имеет разновидности, которую позволяют исключить из последовательности наборы элементов:
minus(elements: Array<T>): Sequence<T> minus(elements: Iterable<T>): Sequence<T> minus(elements: Sequence<T>): Sequence<T>
Промежуточная операция
plus(element: T): Sequence<T>
Возвращает новую последовательность, которая содержит все элементы текущей за исключением плюс элемент element.
Имеет разновидности, которую позволяют включить в последовательность наборы элементов:
plus(elements: Array<T>): Sequence<T> plus(elements: Iterable<T>): Sequence<T> plus(elements: Sequence<T>): Sequence<T>
Промежуточная операция
reduce(operation: (acc: S, T) -> S): S
Возвращает значение, которое является результатом действия функции operation над каждым элементом последовательности. Первый параметр функции operation - результат работы функции над предыдущим элементом последовательности, в второй параметр - текущий элемент последовательности.
Терминальная операция
shuffled(): Sequence<T>
Условно перемешивает последовательность
Промежуточная операция
sorted(): Sequence<T>
Сортирует последовательность по возрастанию
Промежуточная операция
sortedBy(selector: (T) -> R?): Sequence<T>
Сортирует последовательность по возрастанию на основе селектора
Промежуточная операция
sortedByDescending(selector: (T) -> R?): Sequence<T>
Сортирует последовательность по убыванию на основе функции-селектора
Промежуточная операция
sortedDescending(): Sequence<T>
Сортирует последовательность по убыванию
Промежуточная операция
sum(): Int
Возвращает сумму элементов последовательности.
Терминальная операция
sumOf(selector: (T) -> Int): Int
Возвращает сумму элементов последовательности на основе функции-селектора
Терминальная операция
take(n: Int): Sequence<T>
Возвращает новую последовательность, которая содержит n первых элементов текущей последовательности
Промежуточная операция
takeWhile(predicate: (T) -> Boolean): Sequence<T>
Возвращает новую последовательность, которая содержит n первых элементов текущей последовательности, соответствующих функции-предикату
Промежуточная операция
toHashSet(): HashSet<T>
Создает из последовательности объект HashSet
Терминальная операция
toList(): List<T>
Создает из последовательности объект List
Терминальная операция
toMap(): Map<K, V>
Создает из последовательности объект Map
Терминальная операция
toSet(): Set<T>
Создает из последовательности объект Set
Терминальная операция