Adding an element to the end of the stream for each element already in the stream

For the function Function<T, T> fand Stream<T> ts, which is a good (good readability, good performance) way to create a new Stream<T>one that first contains the original elements, and then the elements converted to f.

You might think this would work:

Stream.concat(ts, ts.map(f));

But this does not work and throws an exception:

java.lang.IllegalStateException: stream has already been operated upon or closed

Note: that order matters: the original elements must be first in the correct order, and then the converted elements in order of coincidence.

+6
source share
3 answers

, .

, , , , .

"" . .

, , " ",

Stream.concat(someList.stream(), someList.stream().map(f));

. , , , :

List<Whatever> someList = ts.collect(Collectors.asList());

.

+7

Spliterator, . "" , , :

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

, , extras .

, . .

, .

+6

, ( , ), flatMap:

Stream<T> s = ...;
Stream<T> result = s.flatMap(x -> Stream.of(x, f.apply(x));
result.forEach(System.out::println);

, , , ...

+3

All Articles