Итерабельность и последовательность Котлина выглядят точно так же. Почему требуются два типа?

Оба этих интерфейса определяют только один метод

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 } print("before sum ") val sum = seqMapped.sum() 

      Он печатает:

       before sum 1 2 

      Sequence<T> предназначена для ленивого использования и эффективной конвейерной обработки, когда вы хотите как можно больше сократить работу, выполняемую в терминальных операциях, так же, как и в потоках Java. Тем не менее, лень вводит некоторые накладные расходы, что нежелательно для простых простых преобразований небольших коллекций и делает их менее эффективными.

      В общем, нет хорошего способа определить, когда это необходимо, поэтому в 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() .

    Завершение ответа «горячей клавиши»:

    Важно заметить, что итерации последовательностей и итераций считали ваши элементы:

    Пример последовательности:

      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") } 

    фильтр – фильтр – Карта – Карта – Каждый – Каждый