Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 398
BambooleanLogic Avatar asked Oct 16 '14 11:10

BambooleanLogic


People also ask

How do I skip the last element in stream?

Using Stream skip() Method: The skip() method returns a stream after removing first N elements. Therefore this method can be used to skip the elements except the last one. Approach: Get the stream of elements in which the last element is to be returned.

How do I use skip in stream?

The skip() MethodThe skip(n) method is an intermediate operation that discards the first n elements of a stream. The n parameter can't be negative, and if it's higher than the size of the stream, skip() returns an empty stream. When this stream is executed, the forEach starts asking for items.

How do I skip the first element in a stream?

Stream skip(n) method is used to skip the first 'n' elements from the given Stream. The skip() method returns a new Stream consisting of the remaining elements of the original Stream, after the specified n elements have been discarded in the encounter order.

What is the purpose of the limit () method in Java 8?

The limit method of the Stream class introduced in Java 8 allows the developer to limit the number of elements that will be extracted from a stream. The limit method is useful in those applications where the user wishes to process only the initial elements that occur in the stream.


1 Answers

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);
}
like image 161
Holger Avatar answered Oct 24 '22 00:10

Holger