web-dev-qa-db-fra.com

Ajout de nouvelle valeur au flux existant

Existe-t-il un bon moyen d'ajouter une nouvelle valeur à l'existant Stream? Tout ce que je peux imaginer, c'est quelque chose comme ça:

public <T> Stream<T> addToStream(Stream<T> stream, T elem ) {
    List<T> result = stream.collect(Collectors.toList());
    result.add(elem);
    return result.stream();
}

Mais je cherche quelque chose de plus concis que je puisse utiliser dans l'expression lambda sans verbosité.

Une autre question est apparue lorsque j'ai essayé de mettre en œuvre le principe PECS :

public <T> Stream<? super T> addToStream(Stream<? super T> stream, T elem ) {
    List<? super T> result = stream.collect(Collectors.toList()); //error
    result.add(elem);
    return result.stream();
}

On dirait que le joker ne fonctionne pas avec Stream.collect et je me demande pourquoi. Merci d'avance.

70
Dmytro

La question cache une hypothèse incorrecte: que les flux contiennent réellement leurs données. Ils ne; les flux ne sont pas des structures de données, ils permettent de spécifier des opérations en bloc sur diverses sources de données.

Il existe des combinateurs permettant de combiner deux flux en un seul, tel que Stream.concat, et des usines pour créer des flux à partir d’un ensemble d’éléments connus (Stream.of) ou de collections (Collection.stream). Vous pouvez donc les combiner si vous souhaitez générer un nouveau flux, concaténant le flux que vous avez en main, ainsi qu’un nouveau flux décrivant les nouveaux éléments.

Le problème dans votre exemple PECS est que vous avez trois occurrences de ? super T, et vous supposez qu'ils décrivent le même type, mais ils ne le font pas. Chaque occurrence d'un caractère générique correspond à une capture unique, ce qui n'est pas ce que vous voulez; vous devez donner un nom à cette variable de type pour que le compilateur sache que le type de la liste et le type du flux d'entrée sont identiques. (En outre, ne matérialisez pas une collection; cela coûte cher et risque de ne pas se terminer si le flux n'est pas fini. Utilisez simplement concat.) La réponse est donc: vous venez de vous tromper de génériques. Voici une façon de le faire:

public<T> Stream<T> appendToStream(Stream<? extends T> stream, T element) {
    return Stream.concat(stream, Stream.of(element));
}

Vous vous êtes confondu avec PECS parce que vous pensiez "insérer" dans le flux, alors qu'en fait vous en consommez.

85
Brian Goetz

Que diriez-vous

return Stream.concat(stream, Stream.of(elem));

c'est en supposant que le flux original est fini. Si ce n'est pas le cas, vous pouvez les concaténer dans l'ordre inverse.

29
Eran

La bibliothèque StreamEx possède les méthodes #prepend() et #append() appropriées. Voici un exemple d'utilisation

StreamEx.of("second").prepend("first").append("third").forEach(System.out::println);

Une sortie est la suivante:

first
second
third
1
artspb
  1. Convertir un flux en liste
  2. Ajouter un nouvel élément
  3. Reconvertir en flux
0
kreker

La meilleure façon consiste à utiliser un flatMap comme suit:

public <T> Stream<T> appendToStream(Stream<T> stream, T element) {
    return stream.flatMap(e -> Stream.of(e, element));
}

Cela fonctionne sur le flux d'origine et peut donc être simplement une autre opération intermédiaire sur le flux, par exemple:

    stream.flatMap(e -> Stream.of(e, element))
            .map(...)
            .filter(...)
            .collect(Collectors.toList());
0
Solubris