如果我有Stream<T>
,我可以轻松使用skip(long)
跳过流的前几个元素。但是,在流的末尾跳过给定数量的元素似乎没有等价物。
最明显的解决方案是使用limit(originalLength - elementsToRemoveAtEnd)
,但这需要事先知道初始长度,但情况并非总是如此。
有没有办法删除未知长度的流的最后几个元素而不必将其收集到Collection
中,计算元素并再次流式传输?
答案 0 :(得分:9)
Stream
没有一般无存储空间的解决方案可能长度未知。但是,您不需要收集整个流,只需要一个与要跳过的元素数量一样大的存储空间:
static <T> Stream<T> skipLastElements(Stream<T> s, int count) {
if(count<=0) {
if(count==0) return s;
throw new IllegalArgumentException(count+" < 0");
}
ArrayDeque<T> pending=new ArrayDeque<T>(count+1);
Spliterator<T> src=s.spliterator();
return StreamSupport.stream(new Spliterator<T>() {
public boolean tryAdvance(Consumer<? super T> action) {
while(pending.size()<=count && src.tryAdvance(pending::add));
if(pending.size()>count) {
action.accept(pending.remove());
return true;
}
return false;
}
public Spliterator<T> trySplit() {
return null;
}
public long estimateSize() {
return src.estimateSize()-count;
}
public int characteristics() {
return src.characteristics();
}
}, false);
}
public static void main(String[] args) {
skipLastElements(Stream.of("foo", "bar", "baz", "hello", "world"), 2)
.forEach(System.out::println);
}
答案 1 :(得分:1)
以下代码使用ArrayDeque
缓冲n
个元素,其中n
是最后要跳过的元素数。诀窍是使用skip(n)
。这会导致第一个n
元素添加到ArrayDeque
。然后,一旦n
个元素被缓冲,流就会继续处理元素,但会弹出ArrayDeque
的元素。到达流的末尾时,最后n
个元素卡在ArrayDeque
中并被丢弃。
ArrayDeque
不允许null
元素。在添加到null
之前,下面的代码会将NULL_VALUE
映射到ArrayDeque
,然后在从NULL_VALUE
弹出后将null
映射回ArrayDeque
。
private static final Object NULL_VALUE = new Object();
public static <T> Stream<T> skipLast(Stream<T> input, int n)
{
ArrayDeque<T> queue;
if (n <= 0)
return(input);
queue = new ArrayDeque<>(n + 1);
input = input.
map(item -> item != null ? item : NULL_VALUE).
peek(queue::add).
skip(n).
map(item -> queue.pop()).
map(item -> item != NULL_VALUE ? item : null);
return(input);
}