L'itérable et la séquence de Kotlin sont exactement les mêmes. Pourquoi deux types requis?
Ces deux interfaces ne définissent qu'une seule méthode
public operator fun iterator(): Iterator<T>
La Documentation dit que Sequence
est censé être paresseux. Mais n'est-ce pas Iterable
paresseux aussi (sauf si soutenu par un Collection
)?
2 réponses
La principale différence réside dans la sémantique et l'implémentation des fonctions d'extension stdlib pour Iterable<T>
et Sequence<T>
.
-
Pour
Sequence<T>
, les fonctions d'extension fonctionnent paresseusement lorsque cela est possible, de la même manière que les opérations Java Streams intermediate . Par exemple,Sequence<T>.map { ... }
renvoie un autreSequence<R>
et ne traite pas réellement les éléments jusqu'à ce qu'une opérationterminal CommetoList
oufold
soit appelée.Considérez ceci code:
val seq = sequenceOf(1, 2) val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate print("before sum ") val sum = seqMapped.sum() // terminal
, Il imprime:
before sum 1 2
Sequence<T>
est destiné à une utilisation paresseuse et à un pipelining efficace lorsque vous souhaitez réduire le travail effectué dans les opérations terminal autant que possible, de même que les flux Java. Cependant, la paresse introduit des frais généraux, ce qui n'est pas souhaitable pour les transformations simples courantes de petites collections et les rend moins performantes.En général, il n'y a pas de bon moyen de déterminer quand il est nécessaire, donc dans Kotlin stdlib la paresse est explicite et extrait à l'
Sequence<T>
interface d'éviter de l'utiliser sur tous lesIterable
s par défaut. -
Pour
Iterable<T>
, au contraire, les fonctions d'extension avecsémantique d'opération intermédiaire fonctionnent avec impatience, traitent les éléments immédiatement et renvoient un autreIterable
. Par exemple,Iterable<T>.map { ... }
renvoie unList<R>
avec les résultats du mappage.Le code équivalent pour itérable:
val lst = listOf(1, 2) val lstMapped: List<Int> = lst.map { print("$it "); it * it } print("before sum ") val sum = lstMapped.sum()
Cela imprime:
1 2 before sum
Comme indiqué ci-dessus,
Iterable<T>
est non-paresseux par défaut, et cette solution se montre bien: dans la plupart des cas, elle a une bonne localité de référence tirant ainsi parti du cache CPU, de la prédiction, de la pré-extraction, etc. de sorte que même la copie multiple d'une collection fonctionne encore assez bien et fonctionne mieux dans les cas simples avec de petites collections.Si vous avez besoin de plus de contrôle sur le pipeline d'évaluation, il y a une conversion explicite en une séquence paresseuse avec
Iterable<T>.asSequence()
fonction.
Remplir la réponse du raccourci clavier:
Il est important de remarquer comment Sequence et Iterable iterates pensaient vos éléments:
Exemple de Séquence:
list.asSequence()
.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
Résultat du Journal:
Filtre - Carte - Chacune; filtre - Carte - Chaque
Exemple itérable:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
Filtre - filtre - carte - Carte - Chaque - Chaque