给定一个函数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
注意:订单确实很重要:原始元素必须以正确的顺序排在第一位,然后按顺序排列转换后的元素。
答案 0 :(得分:7)
你不能打开一瓶葡萄酒,然后将瓶子交给另一个人并要求他再次打开。
因此,我认为流的 nature 不可能做你要求的事情。
每个流都有一个“处理”链。你不能拥有两个。
所以你能得到的最接近的就是“它的起源”,比如
Stream.concat(someList.stream(), someList.stream().map(f));
例如,。当然,当你没有这个清单时,你可以选择:
List<Whatever> someList = ts.collect(Collectors.asList());
第一
答案 1 :(得分:6)
您可以实现包装源流的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
时,可能取决于您的用例是否在内存消耗方面足够高效。
在实际流处理之前收集和映射的优势在于,您只需遍历一次源。当检索元素需要很长时间或者元素的顺序可以在流之间改变时,这可能会有所帮助。
您还可以在复制之前首先将某些流操作链接到源,同样无需将中间结果收集到集合中。
答案 2 :(得分:3)
当它仅用于元素而不用于顺序(首先是原始项目,然后是修改过的),你可以使用flatMap:
Stream<T> s = ...;
Stream<T> result = s.flatMap(x -> Stream.of(x, f.apply(x));
result.forEach(System.out::println);
如果订单相关,可以问你为什么要使用流,因为你不会从懒惰的评估中受益......