Est-il possible de réutiliser un Stream?

j'apprends les nouvelles fonctionnalités de Java 8, et en expérimentant avec des flux ( java.util.stream.Stream ) et des collecteurs, j'ai réalisé qu'un flux ne peut pas être utilisé deux fois.

y a-t-il un moyen de le réutiliser?

40
demandé sur Lii 2016-03-28 05:22:39

6 réponses

De la documentation :

un cours d'eau ne doit être exploité (en invoquant un cours d'eau intermédiaire ou terminal) qu'une seule fois.

une implémentation stream peut jeter IllegalStateException si elle détecte que le stream est réutilisé.

Si la réponse est non, les ruisseaux ne sont pas destinés à être réutilisés.

44
répondu Vince Emigh 2016-03-28 02:32:46

si vous voulez avoir l'effet de réutiliser un flux, vous pouvez envelopper l'expression de flux dans un fournisseur et appeler myStreamSupplier.get() chaque fois que vous voulez un nouveau. Par exemple:

Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());
42
répondu Hank D 2018-06-04 09:47:14

comme d'autres l'ont dit,"non tu ne peux pas".

mais il est utile de se souvenir de la pratique summaryStatistics() pour de nombreuses opérations de base:

donc au lieu de:

List<Person> personList = getPersons();

personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();

, Vous pouvez:

// Can also be DoubleSummaryStatistics from mapToDouble()

IntSummaryStatistics stats = personList.stream()
                                       .mapToInt(p-> p.getAge())
                                       .summaryStatistics();

stats.getAverage();
stats.getMin();
stats.getMax();
15
répondu Andrejs 2017-04-12 08:46:17

l'idée générale du Stream est qu'il est une fois-off. Cela vous permet de créer des sources non-réentérables (par exemple, lire les lignes à partir de la connexion réseau) sans stockage intermédiaire. Cependant, si vous voulez réutiliser le contenu du flux, vous pouvez le transférer dans la collection intermédiaire pour obtenir la "copie papier":

Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list

si vous ne voulez pas matérialiser le flux, dans certains cas il y a des moyens de faire plusieurs choses avec le même flux à la fois. Par exemple, vous pouvez vous référer à cette ou cette "question 151950920" pour plus de détails.

3
répondu Tagir Valeev 2017-05-23 11:54:53

comme d'autres l'ont noté, l'objet stream lui-même ne peut pas être réutilisé.

mais une façon d'obtenir l'effet de réutiliser un flux est de extraire le code de création de flux à une fonction .

vous pouvez le faire en créant une méthode ou un objet de fonction qui contient le code de création de flux. Vous pouvez alors l'utiliser plusieurs fois.

exemple:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // The normal way to use a stream:
    List<String> result1 = list.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    // The stream operation can be extracted to a local function to
    // be reused on multiple sources:
    Function<List<Integer>, List<String>> listOperation = l -> l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    List<String> result2 = listOperation.apply(list);
    List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));

    // Or the stream operation can be extracted to a static method,
    // if it doesn't refer to any local variables:
    List<String> result4 = streamMethod(list);

    // The stream operation can also have Stream as argument and return value,
    // so that it can be used as a component of a longer stream pipeline:
    Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i);

    List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
        .filter(s -> s.length() < 7)
        .sorted()
        .collect(toCollection(LinkedList::new));
}

public static List<String> streamMethod(List<Integer> l) {
    return l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());
}

si, d'un autre côté, vous avez déjà un objet stream que vous voulez itérer plusieurs fois, alors vous devez enregistrer le contenu du stream dans un objet collection.

vous pouvez alors obtenir plusieurs flux avec le même contenu de que collection.

exemple:

public void test(Stream<Integer> stream) {
    // Create a copy of the stream elements
    List<Integer> streamCopy = stream.collect(toList());

    // Use the copy to get multiple streams
    List<Integer> result1 = streamCopy.stream() ...
    List<Integer> result2 = streamCopy.stream() ...
}
1
répondu Lii 2018-10-03 08:01:03

en y repensant, cette volonté de" réutiliser " un flux est juste la volonté de réaliser le résultat souhaité avec une opération en ligne agréable. Donc, en gros, ce dont nous parlons ici, c'est de ce que nous pouvons faire pour continuer le traitement après avoir écrit une opération de terminal?

1) si votre terminal retourne une collection , le problème est résolu tout de suite, puisque chaque collection peut être retournée dans un flux (JDK 8).

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .collect(Collectors.toList())
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

2) si vos opérations de terminal retourne un optionnel , avec les améliorations JDK 9 à la classe optionnelle, vous pouvez transformer le résultat optionnel en un flux, et obtenir le bon fonctionnement en ligne désiré:

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .findAny()
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

3) si votre terminal retourne something else , je doute vraiment que vous devriez considérer un flux pour traiter un tel résultat:

List<Integer> l=Arrays.asList(5,10,14);
        boolean allEven=l.stream()
            .filter(nth-> nth>5)
            .allMatch(nth-> nth%2==0);
        if(allEven){
            ...
        }
0
répondu BabaNew 2018-06-04 09:42:44