Kotlin's Iterable y Sequence se ven exactamente igual. ¿Por qué se requieren dos types?

Ambas interfaces definen solo un método

public operator fun iterator(): Iterator<T> 

La documentation dice que Sequence debe ser flojo. Pero, ¿no es Iterable perezoso también (a less que esté respaldado por una Collection )?

    La diferencia key radica en la semántica y la implementación de las funciones de extensión stdlib para Iterable<T> y Sequence<T> .

    • Para Sequence<T> , las funciones de extensión se ejecutan perezosamente siempre que sea posible, de forma similar a las operaciones intermedias de Java Streams. Por ejemplo, Sequence<T>.map { ... } devuelve otra Sequence<R> y en realidad no procesa los elementos hasta que se toList una operación de terminal como toList o fold .

      Considera este código:

       val seq = sequenceOf(1, 2) val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } print("before sum ") val sum = seqMapped.sum() 

      Imprime:

       before sum 1 2 

      Sequence<T> está pensada para el uso diferido y la canalización eficiente cuando se desea networkingucir el trabajo realizado en operaciones de terminal tanto como sea posible, lo mismo para Java Streams. Sin embargo, la pereza introduce cierta sobrecarga, lo cual es indeseable para las transformaciones simples comunes de collections más pequeñas y las hace less efectivas.

      En general, no hay una buena forma de determinar cuándo es necesario, por lo que en Kotlin stdlib la pereza se explicita y se extrae en la interfaz Sequence<T> para evitar su uso en todos los Iterable s por defecto.

    • Para Iterable<T> , por el contrario, las funciones de extensión con semántica de operación intermedia funcionan con entusiasmo, procesa los elementos de inmediato y devuelve otro Iterable . Por ejemplo, Iterable<T>.map { ... } devuelve una List<R> con los resultados de la asignación.

      El código equivalente para Iterable:

       val lst = listOf(1, 2) val lstMapped: List<Int> = lst.map { print("$it "); it * it } print("before sum ") val sum = lstMapped.sum() 

      Esto se imprime:

       1 2 before sum 

      Como se dijo anteriormente, Iterable<T> no es perezosa por defecto, y esta solución se muestra bien: en la mayoría de los casos tiene una buena localidad de reference , aprovechando el caching, la pnetworkingicción, la recuperación previa de la CPU, etc. de modo que incluso la copy múltiple de un la colección aún funciona lo suficientemente bien y funciona mejor en casos simples con collections pequeñas.

      Si necesita más control sobre la canalización de evaluación, hay una conversión explícita a una secuencia diferida con la function Iterable<T>.asSequence() .