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)?

49
demandé sur hotkey 2016-02-25 16:48:52

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 autre Sequence<R> et ne traite pas réellement les éléments jusqu'à ce qu'une opérationterminal Comme toList ou fold 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 les Iterables 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 autre Iterable. Par exemple, Iterable<T>.map { ... } renvoie un List<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.

94
répondu hotkey 2018-03-12 11:55:03

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

23
répondu Leandro Borges Ferreira 2018-01-12 16:53:39