Skip last x elements in Stream<T>

自古美人都是妖i 提交于 2019-12-01 16:12:41

There is no general storage-free solution for Streams that may have an unknown length. However, you don’t need to collect the entire stream, you only need a storage as large as the number of elements you want to skip:

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);
}

The following code uses ArrayDeque to buffer n elements where n is the number of elements to skip at the end. The trick is to use skip(n). This causes the first n elements to be added to the ArrayDeque. Then, once n elements have been buffered, the stream continues processing elements but pops elements from ArrayDeque. When the end of the stream is reached, the last n elements are stuck in the ArrayDeque and discarded.

ArrayDeque does not allow null elements. The code below maps null into NULL_VALUE before adding to ArrayDeque and then maps NULL_VALUE back to null after popping from 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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!