Hinzufügen eines neuen Werts zum vorhandenen Stream

79

Gibt es eine gute Möglichkeit, dem Bestehenden einen neuen Wert hinzuzufügen Stream? Ich kann mir nur so etwas vorstellen:

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

Aber ich suche etwas Prägnanteres, das ich ohne Ausführlichkeit im Lambda-Ausdruck verwenden kann.

Eine andere Frage tauchte auf, als ich versuchte, das PECS- Prinzip umzusetzen :

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();
}

Scheint, als würde Wildcard nicht funktionieren Stream.collectund ich frage mich warum. Danke im Voraus.

Dmytro
quelle
2
Es gibt keine Garantie dafür, dass die Listvon collect(Collectors.toList())Support zurückgegebenen addDaten verwendet werden. Collectors.toCollectionStattdessen können Sie den Listentyp auswählen, den Sie erhalten.
Alex - GlassEditor.com

Antworten:

97

Die Frage widerlegt eine falsche Annahme: Streams enthalten tatsächlich ihre Daten. Sie nicht; Streams sind keine Datenstrukturen, sondern ein Mittel zum Spezifizieren von Massenoperationen in einer Vielzahl von Datenquellen.

Es gibt Kombinatoren zum Kombinieren von zwei Streams zu einem, z. B. Stream.concatFabriken zum Erstellen von Streams aus einer Reihe bekannter Elemente ( Stream.of) oder aus Sammlungen ( Collection.stream). Sie können diese also kombinieren, wenn Sie einen neuen Stream erstellen möchten, der die Verkettung des vorhandenen Streams darstellt, sowie einen neuen Stream, der die neuen Elemente beschreibt.

Das Problem in Ihrem PECS-Beispiel besteht darin, dass Sie drei Vorkommen ? super Thaben und davon ausgehen, dass sie denselben Typ beschreiben, dies jedoch nicht. Jedes Auftreten eines Platzhalters entspricht einer eindeutigen Erfassung, die nicht Ihren Wünschen entspricht. Sie müssen dieser Typvariablen einen Namen geben, damit der Compiler weiß, dass der Typ der Liste und der Typ des Eingabestreams identisch sind. (Materialisieren Sie auch keine Sammlung; das ist teuer und möglicherweise nicht terminierbar, wenn der Stream nicht endlich ist. Verwenden Sie einfach concat.) Die Antwort lautet also: Sie haben gerade die Generika falsch verstanden. Hier ist eine Möglichkeit, dies zu tun:

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

Sie haben sich mit PECS verwechselt, weil Sie darüber nachgedacht haben, in den Stream einzufügen, obwohl Sie tatsächlich davon konsumieren.

Brian Goetz
quelle
7
Die Methode sollte nicht "appendToStream" heißen? Ansonsten denke ich, dass die Parameter der Concat-Methode umgeschaltet werden sollten.
Andrea Polci
concatMethode kann verwenden, aber nicht zu viel, von Javadoc: Seien Sie vorsichtig, wenn Sie Streams aus wiederholter Verkettung erstellen. Der Zugriff auf ein Element eines stark verketteten Streams kann zu tiefen Aufrufketten oder sogar zu StackOverflowException führen.
TongChen
34

Wie wäre es mit

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

Dies setzt voraus, dass der ursprüngliche Stream endlich ist. Wenn dies nicht der Fall ist, können Sie sie in umgekehrter Reihenfolge zusammenfassen.

Eran
quelle
Seien Sie vorsichtig, wenn Sie Streams aus wiederholter Verkettung erstellen. Der Zugriff auf ein Element eines stark verketteten Streams kann zu tiefen Aufrufketten oder sogar zu StackOverflowException führen.
TongChen
5

Die StreamEx Bibliothek entspricht #prepend()und #append()Methoden. Hier ist ein Beispiel, wie sie verwendet werden können:

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

Eine Ausgabe lautet wie folgt:

first
second
third
artspb
quelle
Nett! Allerdings eine andere Bibliothek.
GhostCat
0

Am besten verwenden Sie eine flatMap wie folgt:

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

Dies funktioniert auf dem ursprünglichen Stream, so dass es sich nur um eine weitere Zwischenoperation auf dem Stream handeln kann, z.

    stream.flatMap(e -> Stream.of(e, element))
            .map(...)
            .filter(...)
            .collect(Collectors.toList());
Solubris
quelle
Wie oft möchten Sie ein Element hinzufügen? : D Unze für Stream oder einmal pro Element im Original-Stream?
Benutzer482745