I am wondering is there an alternative to
List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size());
with stream usage?
Getting the Last Element of an Infinite Stream Stream<Integer> stream = Stream. iterate(0, i -> i + 1); stream. reduce((first, second) -> second). orElse(null);
Using Stream findFirst() Method: The findFirst() method will returns the first element of the stream or an empty if the stream is empty. Approach: Get the stream of elements in which the first element is to be returned. To get the first element, you can directly use the findFirst() method.
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.
We can use the remove() method of ArrayList container in Java to remove the last element. ArrayList provides two overloaded remove() method: remove(int index) : Accept index of the object to be removed. We can pass the last elements index to the remove() method to delete the last element.
Use Stream.skip()
Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned.
all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething);
A custom collector can be written like this:
public static <T> Collector<T, ?, List<T>> lastN(int n) {
return Collector.<T, Deque<T>, List<T>>of(ArrayDeque::new, (acc, t) -> {
if(acc.size() == n)
acc.pollFirst();
acc.add(t);
}, (acc1, acc2) -> {
while(acc2.size() < n && !acc1.isEmpty()) {
acc2.addFirst(acc1.pollLast());
}
return acc2;
}, ArrayList::new);
}
And use it like this:
List<String> lastTen = input.stream().collect(lastN(10));
In case the stream has unknown size, there's probably no way around consuming the entire stream and buffering the last n
elements encountered so far. You can do this using some kind of deque, or a specialized ring-buffer automatically maintaining its maximum size (see this related question for some implementations).
public static <T> List<T> lastN(Stream<T> stream, int n) {
Deque<T> result = new ArrayDeque<>(n);
stream.forEachOrdered(x -> {
if (result.size() == n) {
result.pop();
}
result.add(x);
});
return new ArrayList<>(result);
}
All of those operations (size
, pop
, add
) should have complexity of O(1), so the overall complexity for a stream with (unknown) length n would be O(n).
Sometimes I need a "oneliner" (in this case a three liner) as creating a collector is just too much fuss.
If the stream is small then it is possible to reverse
, limit
and reverse
again without much sacrificing performance. This will result the last n elements.
It is useful if filtering is required as in that case it is not possible to specify the size.
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter(i -> i % 2 == 0)
.sorted(Comparator.reverseOrder())
.limit(2)
.sorted(Comparator.naturalOrder())
.forEach(System.out::println); // prints 6 8
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With