Ambas interfaces definen solo un método
public operator fun iterator(): Iterator<T>
La documentación dice que Sequence
está destinado a ser vago. ¿Pero no es Iterable
perezoso también (a menos que esté respaldado por una Collection
)?
fuente
Ambas interfaces definen solo un método
public operator fun iterator(): Iterator<T>
La documentación dice que Sequence
está destinado a ser vago. ¿Pero no es Iterable
perezoso también (a menos que esté respaldado por una Collection
)?
La diferencia clave radica en la semántica y la implementación de las funciones de extensión stdlib para Iterable<T>
y Sequence<T>
.
Porque Sequence<T>
, las funciones de extensión funcionan de manera perezosa cuando es posible, de manera similar a las operaciones intermedias de Java Streams . Por ejemplo, Sequence<T>.map { ... }
devuelve otro Sequence<R>
y en realidad no procesa los elementos hasta que se llama a una operación de terminal como toList
o fold
.
Considere este código:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
Imprime:
before sum 1 2
Sequence<T>
está diseñado para un uso diferido y una canalización eficiente cuando desea reducir el trabajo realizado en las operaciones de la terminal tanto como sea posible, al igual que Java Streams. Sin embargo, la pereza introduce algunos gastos generales, que no son deseables para las transformaciones simples comunes de colecciones más pequeñas y las hace menos eficaces.
En general, no hay una buena manera de determinar cuándo se necesita, por lo que en Kotlin stdlib la pereza se hace explícita y se extrae a la Sequence<T>
interfaz para evitar usarla en todos los Iterable
s por defecto.
Porque Iterable<T>
, por el contrario, las funciones de extensión con semántica de operación intermedia trabajan con entusiasmo, procesan los elementos de inmediato y devuelven otro Iterable
. Por ejemplo, Iterable<T>.map { ... }
devuelve a 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 imprime:
1 2 before sum
Como se dijo anteriormente, Iterable<T>
no es perezoso de forma predeterminada, y esta solución se muestra bien: en la mayoría de los casos, tiene una buena localidad de referencia , aprovechando así el caché de la CPU, la predicción, la búsqueda previa, etc., de modo que incluso la copia múltiple de una colección sigue funcionando bien. suficiente y funciona mejor en casos simples con colecciones pequeñas.
Si necesita más control sobre la canalización de evaluación, hay una conversión explícita a una secuencia perezosa con Iterable<T>.asSequence()
función.
Java
(en su mayoríaGuava
)map
,filter
y otras no contienen suficiente información para decidir más allá del tipo de colección de origen, y dado que la mayoría de las colecciones también son Iterables, ese no es un buen marcador para "ser perezoso" porque es comúnmente EN TODAS PARTES. perezoso debe ser explícito para estar seguro.Completando la respuesta de la tecla de acceso rápido:
Es importante notar cómo Sequence e Iterable iteran a través de sus elementos:
Ejemplo de secuencia:
list.asSequence().filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
Resultado del registro:
filtro - Mapa - Cada uno; filtro - Mapa - Cada
Ejemplo iterable:
list.filter { field -> Log.d("Filter", "filter") field.value > 0 }.map { Log.d("Map", "Map") }.forEach { Log.d("Each", "Each") }
filtro - filtro - Mapa - Mapa - Cada uno - Cada
fuente
Puedes encontrar más aquí .
fuente