Оба этих интерфейса определяют только один метод
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")
}
фильтр - фильтр - карта - карта - каждый - каждый