Оба этих интерфейса определяют только один метод
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
или 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
по умолчанию не использовать ее во всех s.
Для 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()
функцией.
Java
(в основномGuava
) фанатовmap
,filter
и другие, не несут достаточно информации для принятия решения, кроме как из исходного типа коллекции, и, поскольку большинство коллекций также являются Iterable, это не хороший маркер для «лениться», потому что это обычно ВЕЗДЕ. lazy должен быть явным, чтобы быть в безопасности.Завершение ответа горячей клавиши:
Важно заметить, как 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") }
фильтр - фильтр - Карта - Карта - Каждый - Каждый
источник
Вы можете найти больше здесь .
источник