为流中已有的每个元素添加一个元素到流的末尾

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

给定一个函数 Function<T, T> f 和一个 Stream<T> ts 什么是创建新 Stream<T> 的好方法(可读性好,性能好),它首先包含原始元素,然后是由 f.

转换的元素

有人可能认为这可行:

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

但这不起作用并导致异常:

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

注意:顺序很重要:原始元素必须以正确的顺序排在第一位,然后是转换后的元素以匹配的顺序。

你不能打开一瓶酒,然后把瓶子递给另一个人,让他再打开

因此我认为流的 性质 不可能按照您的要求进行操作。

每个流有 一个 链 "processing"。你不能有 两个.

所以最接近的是从 "its origin" 开始工作,比如

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

例如。当然,如果您没有该列表,您可以选择:

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

第一。

当只针对元素而不针对顺序(首先是原始项目,然后是修改后的项目)时,您可以使用 flatMap:

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

如果顺序相关,有人可能会问为什么要使用流,因为您不会从惰性求值中获益...

您可以实现一个 Spliterator 来包装您的源流。在内部,您将为每个已处理的元素创建 "duplicate" 元素,然后在源为空后切换到这些重复项:

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 保留在流构建器中,这在内存消耗方面是否足够有效。

在实际流处理之前进行收集和映射的优势在于您只需遍历源一次。当检索元素需要很长时间或元素的顺序在流之间发生变化时,这可能会有所帮助。

您还可以在复制之前先将一些流操作链接到源,同样无需将中间结果收集到集合中。