Search code examples
javajava-8java-stream

Skip last x elements in Stream<T>


If I have a Stream<T>, I can easily use skip(long) to skip the first few elements of a stream. However, there seems to be no equivalent for skipping a given number of elements at the end of the stream.

The most obvious solution is to use limit(originalLength - elementsToRemoveAtEnd), but that requires knowing the initial length beforehand, which isn't always the case.

Is there a way to remove the last few elements of a stream of unknown length without having to collect it into a Collection, count the elements and stream it again?


Solution

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