104

Is there is a good way to add a new value to existing Stream? All I can imagine is something like this:

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

But I'm looking for something more concise that I can use in lambda expression without verbosity.

Another question appeared when I tried to implement PECS principle:

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

Seems like wildcard doesn't work with Stream.collect and I'm wondering why.

starball
  • 20,030
  • 7
  • 43
  • 238
Dmytro
  • 1,850
  • 3
  • 14
  • 20
  • 2
    There's no guarantee that the `List` returned from `collect(Collectors.toList())` supports `add`, you can use `Collectors.toCollection` instead to choose the type of list you get. – Alex - GlassEditor.com Feb 28 '15 at 20:47

5 Answers5

114

The question belies an incorrect assumption: that streams actually contain their data. They do not; streams are not data structures, they are a means for specifying bulk operations across a variety of data sources.

There are combinators for combining two streams into one, such as Stream.concat, and factories for creating streams from a set of known elements (Stream.of) or from collections (Collection.stream). So you can combine these if you want to produce a new stream that is the concatenation of the stream you have in hand, along with a new stream describing the new elements.

The problem in your PECS example is that you've got three occurrences of ? super T, and you are assuming they describe the same type, but they do not. Each occurrence of a wildcard corresponds to a unique capture, which isn't what you want; you need to give that type variable a name so the compiler knows that the type of the list and the type of the input stream are the same. (Also, don't materialize a collection; that's expensive, and potentially non-terminating if the stream is not finite. Just use concat.) So the answer is: you just got the generics wrong. Here's one way to do it:

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

You confused yourself with PECS because you were thinking about "inserting" into the stream, when in fact you're consuming from it.

Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
  • 7
    The method shouldn't be named "appendToStream"? Otherwise I think the parameters of the concat method should be switched. – Andrea Polci Jul 04 '16 at 08:20
  • `concat` method can use but not to much,from javadoc:Use caution when constructing streams from repeated concatenation. Accessing an element of a deeply concatenated stream can result in deep call chains, or even StackOverflowException. – TongChen Sep 21 '19 at 09:33
49

How about

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

this is assuming the original Stream is finite. If it's not, you can concat them in a reversed order.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • Use caution when constructing streams from repeated concatenation. Accessing an element of a deeply concatenated stream can result in deep call chains, or even StackOverflowException. – TongChen Sep 21 '19 at 09:33
6

The StreamEx library has corresponding #prepend() and #append() methods. Here's an example how they can be used:

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

An output is as follows:

first
second
third
artspb
  • 1,127
  • 1
  • 10
  • 19
0

The best way is using a flatMap as follows:

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

This operates on the original stream so it can be just another intermediate operation on the stream, eg:

    stream.flatMap(e -> Stream.of(e, element))
            .map(...)
            .filter(...)
            .collect(Collectors.toList());
Solubris
  • 3,603
  • 2
  • 22
  • 37
-1

use stream builder like this

 public static void main(String[] args) {
    Stream.Builder<Integer> builder = Stream.builder();
    builder.add(1).add(2).add(3).add(4).add(5).add(6);
    Stream<Integer> newStream = builder.build();
    newStream.forEach(System.out::println);
}
Mugeesh Husain
  • 394
  • 4
  • 13