web-dev-qa-db-fra.com

Conversion d'Iterable en flux à l'aide de Java 8 JDK

J'ai une interface qui retourne Java.lang.Iterable<T>.

J'aimerais manipuler ce résultat à l'aide de l'API Java 8 Stream.

Cependant, Iterable ne peut pas "diffuser".

Avez-vous une idée de l'utilisation d'Iterable en tant que flux sans le convertir en liste?

389
rayman

Il existe une bien meilleure réponse que d'utiliser spliteratorUnknownSize directement, ce qui est à la fois plus simple et procure un meilleur résultat. Iterable a une méthode spliterator(), vous devriez donc l'utiliser pour obtenir votre spliterator. Dans le pire des cas, il s'agit du même code (l'implémentation par défaut utilise spliteratorUnknownSize), mais dans le cas le plus courant où votre Iterable est déjà une collection, vous obtiendrez un meilleur spliterator, et donc 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 constater, obtenir un flux d'un Iterable (voir aussi cette question ) n'est pas très douloureux.

525
Brian Goetz

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

Streams.stream(iterable)
59
numéro6

Vous pouvez facilement créer un Stream sur un Iterable ou Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
22
nosid

Je voudrais suggérer l’utilisation de la bibliothèque JOOL , elle masque la magie du séparateur derrière l’appel Seq.seq (iterable) et fournit également de nombreuses fonctionnalités utiles supplémentaires.

9
Shaggie

Donc, comme une autre réponse mentionnée, Guava supporte 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 Iterable est de type Collection, ils le lancent.

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
  );
}
4
Alex

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 spliterators et aux booleans (isParallel).

3
g-t

Un moyen très simple de résoudre ce problème consiste à créer une interface Streamable<T> prolongeant Iterable<T> contenant une méthode default <T> stream().

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

Maintenant, n'importe lequel de vos Iterable<T>s peut être rendu trivial simplement en les déclarant implements Streamable<T> au lieu de Iterable<T>.

3
OldCurmudgeon

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

Iterable i = //...
Stream.ofAll(i);
0
Grzegorz Piwowarek