跳过Stream 中的最后x个元素
如果我有一个Stream
,我可以轻松地使用skip(long)
来跳过流的前几个元素。 但是,在流的末尾跳过给定数量的元素似乎没有等价物。
最明显的解决方案是使用limit(originalLength - elementsToRemoveAtEnd)
,但这需要事先知道初始长度,但情况并非总是如此。
有没有办法删除未知长度的流的最后几个元素,而不必将其收集到Collection
,计算元素并再次流动它?
对于可能具有未知长度的Stream
,没有通用的无存储解决方案。 但是,您不需要收集整个流,只需要一个与要跳过的元素数一样大的存储:
static Stream skipLastElements(Stream s, int count) { if(count<=0) { if(count==0) return s; throw new IllegalArgumentException(count+" < 0"); } ArrayDeque pending=new ArrayDeque (count+1); Spliterator src=s.spliterator(); return StreamSupport.stream(new Spliterator () { 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 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); }
以下代码使用ArrayDeque
来缓冲n
元素,其中n
是要在末尾跳过的元素数。 诀窍是使用skip(n)
。 这会导致前n
元素添加到ArrayDeque
。 然后,一旦缓冲了n
元素,流就会继续处理元素,但会弹出ArrayDeque
元素。 当到达流的末尾时,最后n
元素被卡在ArrayDeque
并被丢弃。
ArrayDeque
不允许使用null
元素。 下面的代码在添加到NULL_VALUE
之前将null
映射到ArrayDeque
,然后在从ArrayDeque
弹出ArrayDeque
映射回null
。
private static final Object NULL_VALUE = new Object(); public static Stream skipLast(Stream input, int n) { ArrayDeque 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); }