Последовательности

Последнее обновление: 04.03.2024

Наряду с коллекциями Kotlin предоставляет еще один тип наборов элементов – последовательности (sequences). Последовательности предоставляют похожую функциональность, что и интерфейс Iterable, который реализуется типами коллекций. Ключевая разница состоит в том, как обрабатываются элементы последовательности при применении к ним набора операций.

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

Функция sequenceOf

Последовательности представляют интерфейс Sequence<T>. Для создания объекта данного типа можно использовать встроенную функцию sequenceOf(). В качестве параметра она принимает набор элементов, которые будут входить в последовательность. Например:

val people = sequenceOf("Tom", "Sam", "Bob")    //тип Sequence<String>
println(people.joinToString())  // Tom, Sam, Bob

В данном случае определяется последовательность типа Sequence<String>, которая содержит три элемента. Для вывода последовательности на консоль применяется ее преобразование в строку с помощью функции joinToString()

Метод asSequence коллекций

Также можно создать последовательность из объекта 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

Третий способ создания последовательности представляет функция 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

Четвертый способ представляет применение функции 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

    Терминальная операция

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850