Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient way to get the last element of a stream

Stream doesn't have a last() method:

Stream<T> stream; T last = stream.last(); // No such method 

What's the most elegant and/or efficient way to get the last element (or null for an empty Stream)?

like image 589
Bohemian Avatar asked Dec 18 '14 13:12

Bohemian


People also ask

How do I find the last element of a stream?

The other way to get the last element of the stream is by skipping all the elements before it. This can be achieved using Skip function of Stream class. Keep in mind that in this case, we are consuming the Stream twice so there is some clear performance impact.

How do I get one element from a stream list?

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.

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.

Are streams faster than for loops?

Yes, streams are sometimes slower than loops, but they can also be equally fast; it depends on the circumstances. The point to take home is that sequential streams are no faster than loops.


2 Answers

Do a reduction that simply returns the current value:

Stream<T> stream; T last = stream.reduce((a, b) -> b).orElse(null); 
like image 61
Bohemian Avatar answered Sep 27 '22 21:09

Bohemian


This heavily depends on the nature of the Stream. Keep in mind that “simple” doesn’t necessarily mean “efficient”. If you suspect the stream to be very large, carrying heavy operations or having a source which knows the size in advance, the following might be substantially more efficient than the simple solution:

static <T> T getLast(Stream<T> stream) {     Spliterator<T> sp=stream.spliterator();     if(sp.hasCharacteristics(Spliterator.SIZED|Spliterator.SUBSIZED)) {         for(;;) {             Spliterator<T> part=sp.trySplit();             if(part==null) break;             if(sp.getExactSizeIfKnown()==0) {                 sp=part;                 break;             }         }     }     T value=null;     for(Iterator<T> it=recursive(sp); it.hasNext(); )         value=it.next();     return value; }  private static <T> Iterator<T> recursive(Spliterator<T> sp) {     Spliterator<T> prev=sp.trySplit();     if(prev==null) return Spliterators.iterator(sp);     Iterator<T> it=recursive(sp);     if(it!=null && it.hasNext()) return it;     return recursive(prev); } 

You may illustrate the difference with the following example:

String s=getLast(     IntStream.range(0, 10_000_000).mapToObj(i-> {         System.out.println("potential heavy operation on "+i);         return String.valueOf(i);     }).parallel() ); System.out.println(s); 

It will print:

potential heavy operation on 9999999 9999999 

In other words, it did not perform the operation on the first 9999999 elements but only on the last one.

like image 31
Holger Avatar answered Sep 27 '22 19:09

Holger