Оба этих интерфейса определяют только один метод
public operator fun iterator(): Iterator<T>
Документация говорит, что Sequence
означает ленивый. Но не Iterable
ленивый (если не поддерживается Collection
)?
Оба этих интерфейса определяют только один метод
public operator fun iterator(): Iterator<T>
Документация говорит, что Sequence
означает ленивый. Но не Iterable
ленивый (если не поддерживается Collection
)?
Основное различие заключается в семантике и реализации функций расширения stdlib для Iterable<T>
и Sequence<T>
.
Для Sequence<T>
функции расширения выполняются, где это возможно, лениво, подобно промежуточным операциям Java Streams. Например, Sequence<T>.map {... }
возвращает другую Sequence<R>
и фактически не обрабатывает элементы, пока не будет toList
терминальная операция, такая как toList
или fold
.
Рассмотрим этот код:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
Это печатает:
before sum 1 2
Sequence<T>
предназначен для ленивого использования и эффективной конвейеризации, когда вы хотите максимально сократить работу, выполняемую в терминальных операциях, так же, как в Java Streams. Однако лень вносит некоторые накладные расходы, что нежелательно для обычных простых преобразований небольших коллекций и делает их менее производительными.
В общем, нет хорошего способа определить, когда это необходимо, поэтому в Kotlin лень stdlib делается явным и извлекается в интерфейс Sequence<T>
чтобы по умолчанию не использовать его во всех Iterable
.
Для Iterable<T>
, напротив, функции расширения с семантикой промежуточных операций работают с большим удовольствием, сразу же обрабатывают элементы и возвращают другой Iterable
. Например, Iterable<T>.map {... }
возвращает List<R>
с результатами сопоставления в нем.
Эквивалентный код для Iterable:
val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()
Это распечатывает:
1 2 before sum
Как сказано выше, Iterable<T>
по умолчанию не ленив, и это решение хорошо себя зарекомендовало: в большинстве случаев он имеет хорошую локальность ссылок, таким образом, используя преимущества кэша ЦП, прогнозирования, предварительной выборки и т.д., Так что даже многократное копирование коллекция все еще работает достаточно хорошо и работает лучше в простых случаях с небольшими коллекциями.
Если вам нужно больше контроля над конвейером оценки, существует явное преобразование в ленивую последовательность с функцией Iterable<T>.asSequence()
.
Завершение ответа горячей клавиши:
Важно заметить, как Sequence и Iterable итерируют по всем вашим элементам:
Пример последовательности:
list.asSequence()
.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
Зарегистрировать результат:
фильтр - карта - каждая; фильтр - Карта - Каждый
Итерируемый пример:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
фильтр - фильтр - карта - карта - каждый - каждый