Convertir Iterable en Stream à l'aide de Java 8 JDK

420

J'ai une interface qui revient java.lang.Iterable<T>.

Je voudrais manipuler ce résultat en utilisant l'API Java 8 Stream.

Cependant, Iterable ne peut pas "diffuser".

Avez-vous une idée de comment utiliser l'itérable en tant que flux sans le convertir en liste?

rayman
la source
2
Si vous pouvez répéter, pourquoi ne pas utiliser une boucle pour vérifier son état ou sa valeur ou quoi?
Afzaal Ahmad Zeeshan
26
@AfzaalAhmadZeeshan parce que les streams sont bien meilleurs
Sleiman Jneidi
1
Comme je l'ai dit, je dois faire quelques manipulations sur cette liste (filtres, cartographie). Je souhaite utiliser la nouvelle API JDK Java 8 -> Stream. mais Iterable n'est pas "SteamAble"
rayman
Semble bizarre qui myIterable.stream()n'existe pas!
Josh M.
7
@Guillaume: Oui, mais Stream.of(iterable)produit Stream<Iterable<Object>>.
Matthias Braun

Réponses:

573

Il y a une bien meilleure réponse que d'utiliser spliteratorUnknownSizedirectement, ce qui est à la fois plus facile et obtient un meilleur résultat. Iterablea une spliterator()méthode, vous devez donc simplement l'utiliser pour obtenir votre séparateur. Dans le pire des cas, c'est le même code (que l'implémentation par défaut utilise spliteratorUnknownSize), mais dans le cas le plus courant, où votre Iterableest déjà une collection, vous obtiendrez un meilleur séparateur, et donc de meilleures performances de flux (peut-être même un bon parallélisme). C'est aussi moins de code:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

Comme vous pouvez le voir, obtenir un flux depuis un Iterable(voir aussi cette question ) n'est pas très douloureux.

Brian Goetz
la source
109
Une méthode statique sur Streamaurait été bien, par exemple Stream.ofIterable(iterable).
robinst
59
@robinst Ce n'était pas une omission; c'était un choix délibéré (et très débattu). Le défi est que si cela existait, il serait tout simplement trop facile de l'atteindre sans comprendre les compromis qui vont avec. Comme Iterable est si abstrait, une telle méthode entraînerait le flux le moins performant possible (pas de support parallèle, pas d'informations de taille ou de caractéristiques (utilisé pour optimiser les choix d'exécution)). Forcer plus de réflexion se traduit par de meilleures API dans l'ensemble de l'écosystème. Il s'agissait d'un compromis entre «ce qui est le mieux pour le code XYZ» et «ce qui est le mieux pour tout le code Java».
Brian Goetz
2
Sur la base de votre explication, je suis curieux de savoir pourquoi nous avons obtenu Collection.stream () mais pas Iterable.stream (). Il semble que le raisonnement pour omettre Iterable.stream () (ou Stream.ofIterable ()) s'applique également à Collection.stream ().
qualidafial
6
@qualidafial Voir stackoverflow.com/questions/23114015/… .
Brian Goetz
11
@BrianGoetz Il semble que la plupart des gens ne sont pas au niveau pour comprendre ce que vous avez dit ci-dessus ou ne s'en soucient pas. ils veulent seulement écrire le code simple en appelant une API simple. D'un autre côté, ces choses (parallèles ...) peuvent ne pas être importantes pour la plupart des opérations itérables quotidiennes.
user_3380739
71

Si vous pouvez utiliser la bibliothèque Guava, depuis la version 21, vous pouvez utiliser

Streams.stream(iterable)
numéro6
la source
2
Ou, si vous êtes bloqué sur une ancienne version, utilisez Lists.newArrayList(Iterable).
Jacob van Lingen
1
La mise en œuvre actuelle de Guava n'est pas pire que la réponse acceptée: github.com/google/guava/blob/master/guava/src/com/google/common/…
Vadzim
23

Vous pouvez facilement créer un à Streampartir d'un Iterableou Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
la source
2
Vous devez écrire cette fonction une fois, puis l'appeler. Pourquoi un appel pour stream(...)encombrer votre code?
gexicide
1
Je voulais le faire Inline court et élégant .. vous avez raison, je peux écrire cette fonction une fois .. mais je suis en train de supprimer du code (et de ne pas ajouter de code). de toute façon, cette réponse est juste parce que c'est la façon de convertir cela.
rayman
importation statique de cette fonction. court et élégant. (mais pas nécessairement transparent)
aepurniet
9

Je voudrais suggérer d'utiliser la bibliothèque JOOL , elle cache la magie du séparateur derrière l'appel Seq.seq (itérable) et fournit également tout un tas de fonctionnalités utiles supplémentaires.

Shaggie
la source
7

Donc, comme une autre réponse mentionnée, Guava prend en charge cela en utilisant:

Streams.stream(iterable);

Je tiens à souligner que la mise en œuvre fait quelque chose de légèrement différent des autres réponses suggérées. Si le Iterableest de type, Collectionil le lance.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}
Alex
la source
5

J'ai créé cette classe:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

Je pense que c'est parfaitement lisible car vous n'avez pas à penser aux séparateurs et aux booléens (isParallel).

gt
la source
4

Une solution de contournement très simple pour ce problème consiste à créer une Streamable<T>extension d'interface contenant Iterable<T>une default <T> stream()méthode.

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Désormais, n'importe lequel de vos Iterable<T>fichiers peut être simplifié en streaming simplement en les déclarant à la implements Streamable<T>place de Iterable<T>.

OldCurmudgeon
la source
0

Si vous utilisez Vavr (anciennement Javaslang), cela peut être aussi simple que:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
la source
-1

Une autre façon de le faire, avec Java 8 et sans bibliothèques externes:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Greg Witczak
la source