Adding an element to the end of a stream for each element already in the stream
You can't open a bottle of wine and then pass the bottle to another person and ask him to open it again.
Thus I think this is not possible by the nature of streams to do what you ask for.
You have one chain of "processing" per stream. You can't have two.
So the closest you could get at would be working from "its origin", like
Stream.concat(someList.stream(), someList.stream().map(f));
for example. And of course, when you don't have that list, you could go for:
List<Whatever> someList = ts.collect(Collectors.asList());
first.
You can implement a Spliterator
that wraps your source stream. Internally, you will create the "duplicate" element for each processed one, and then switch to those duplicates once the source is empty:
public class Duplicates<T> implements Spliterator<T> {
private Spliterator<T> source;
private Consumer<T> addDuplicate;
private Builder<T> extrasStreamBuilder = Stream.builder();
private Spliterator<T> extrasSpliterator;
private Duplicates(Stream<T> source, UnaryOperator<T> f) {
this.addDuplicate = t -> extrasStreamBuilder.add(f.apply(t));
this.source = source.spliterator();
}
public static <T> Stream<T> of(Stream<T> source, UnaryOperator<T> f) {
return StreamSupport.stream(new Duplicates<>(source, f), false);
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
boolean advanced = false;
if (extrasSpliterator == null) {
advanced = source.tryAdvance(addDuplicate.andThen(action));
}
if (!advanced) {
if (extrasSpliterator == null) {
extrasSpliterator = extrasStreamBuilder.build().spliterator();
}
advanced = extrasSpliterator.tryAdvance(action);
}
return advanced;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
if (extrasSpliterator == null) {
source.forEachRemaining(addDuplicate.andThen(action));
extrasSpliterator = extrasStreamBuilder.build().spliterator();
}
extrasSpliterator.forEachRemaining(action);
}
// other spliterator methods worked with default (Eclipse) implementation for the example below, but should probably delegate to source
}
public static void main(String[] args) {
List<String> input = Arrays.asList("1", "2", "3");
Stream<String> wrapper = Duplicates.of(input.stream(), i -> i + "0");
wrapper.forEach(System.out::println);
}
// Output:
// 1
// 2
// 3
// 10
// 20
// 30
It might depend on your use case whether this is efficient enough in regards of memory consumption as you keep the extras
in the stream builder.
The advantage over collecting and mapping ahead of your actual stream processing is that you only traverse the source once. This might be helpful when retrieving the elements takes long or the order of elements can change between streams.
You are also able to first chain some stream operations to the source before duplicating, again without having to collect the intermediate result into a collection.
When it's only for the elements and not for the order (first the original items, then the modified), you could use flatMap:
Stream<T> s = ...;
Stream<T> result = s.flatMap(x -> Stream.of(x, f.apply(x));
result.forEach(System.out::println);
If the order is relevant, one could ask why you want to use streams, because you won't benefit from lazy evaluation...