向现有 Stream 添加新值

有没有一种好的方法可以给现有的 Stream增加一个新值?我能想到的就是这样的:

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

但我正在寻找一些更简洁的东西,我可以使用在 lambda 表达式没有冗长。

当我试图实施 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();
}

通配符似乎无法与 Stream.collect工作,我想知道为什么。预先感谢。

52070 次浏览

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.

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.

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

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