Get last element of Stream/List in a one-liner

JavaListJava 8Java Stream

Java Problem Overview


How can I get the last element of a stream or list in the following code?

Where data.careas is a List<CArea>:

CArea first = data.careas.stream()
                  .filter(c -> c.bbox.orientationHorizontal).findFirst().get();

CArea last = data.careas.stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .collect(Collectors.toList()).; //how to?

As you can see getting the first element, with a certain filter, is not hard.

However getting the last element in a one-liner is a real pain:

  • It seems I cannot obtain it directly from a Stream. (It would only make sense for finite streams)
  • It also seems that you cannot get things like first() and last() from the List interface, which is really a pain.

I do not see any argument for not providing a first() and last() method in the List interface, as the elements in there, are ordered, and moreover the size is known.

But as per the original answer: How to get the last element of a finite Stream?

Personally, this is the closest I could get:

int lastIndex = data.careas.stream()
        .filter(c -> c.bbox.orientationHorizontal)
        .mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);

However it does involve, using an indexOf on every element, which is most likely not you generally want as it can impair performance.

Java Solutions


Solution 1 - Java

It is possible to get the last element with the method Stream::reduce. The following listing contains a minimal example for the general case:

Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);

This implementations works for all ordered streams (including streams created from Lists). For unordered streams it is for obvious reasons unspecified which element will be returned.

The implementation works for both sequential and parallel streams. That might be surprising at first glance, and unfortunately the documentation doesn't state it explicitly. However, it is an important feature of streams, and I try to clarify it:

  • The Javadoc for the method Stream::reduce states, that it "is not constrained to execute sequentially".
  • The Javadoc also requires that the "accumulator function must be an associative, non-interfering, stateless function for combining two values", which is obviously the case for the lambda expression (first, second) -> second.
  • The Javadoc for reduction operations states: "The streams classes have multiple forms of general reduction operations, called reduce() and collect() [..]" and "a properly constructed reduce operation is inherently parallelizable, so long as the function(s) used to process the elements are associative and stateless."

The documentation for the closely related Collectors is even more explicit: "To ensure that sequential and parallel executions produce equivalent results, the collector functions must satisfy an identity and an associativity constraints."


Back to the original question: The following code stores a reference to the last element in the variable last and throws an exception if the stream is empty. The complexity is linear in the length of the stream.

CArea last = data.careas
                 .stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .reduce((first, second) -> second).get();

Solution 2 - Java

If you have a Collection (or more general an Iterable) you can use Google Guava's

Iterables.getLast(myIterable)

as handy oneliner.

Solution 3 - Java

One liner (no need for stream;):

Object lastElement = list.isEmpty() ? null : list.get(list.size()-1);

Solution 4 - Java

Guava has dedicated method for this case:

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);

It's equivalent to stream.reduce((a, b) -> b) but creators claim it has much better performance.

From [documentation][1]:

> This method's runtime will be between O(log n) and O(n), performing > better on efficiently splittable streams.

It's worth to mention that if stream is unordered this method behaves like findAny(). [1]: https://google.github.io/guava/releases/21.0/api/docs/com/google/common/collect/Streams.html#findLast-java.util.stream.Stream-

Solution 5 - Java

list.stream().sorted(Comparator.comparing(obj::getSequence).reversed()).findFirst().get();

reverse the order and get the first element from the list. here object has sequence number, Comparator provides multiple functionalities can be used as per logic.

Solution 6 - Java

Another way to get the last element is by using sort.

    Optional<CArea> num=data.careas.stream().sorted((a,b)->-1).findFirst();

Solution 7 - Java

You can also use skip() function as below...

long count = data.careas.count();
CArea last = data.careas.stream().skip(count - 1).findFirst().get();

it's super simple to use.

Solution 8 - Java

If you need to get the last N number of elements. Closure can be used. The below code maintains an external queue of fixed size until, the stream reaches the end.

    final Queue<Integer> queue = new LinkedList<>();
    final int N=5;
    list.stream().peek((z) -> {
        queue.offer(z);
        if (queue.size() > N)
            queue.poll();
    }).count();

Another option could be to use reduce operation using identity as a Queue.

    final int lastN=3;
    Queue<Integer> reduce1 = list.stream()
    .reduce( 
        (Queue<Integer>)new LinkedList<Integer>(), 
        (m, n) -> {
            m.offer(n);
            if (m.size() > lastN)
               m.poll();
            return m;
    }, (m, n) -> m);

    System.out.println("reduce1 = " + reduce1);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionskiwiView Question on Stackoverflow
Solution 1 - JavanosidView Answer on Stackoverflow
Solution 2 - JavaPetiView Answer on Stackoverflow
Solution 3 - Javanimo23View Answer on Stackoverflow
Solution 4 - Javak13iView Answer on Stackoverflow
Solution 5 - JavaelesgView Answer on Stackoverflow
Solution 6 - Javasumit singhView Answer on Stackoverflow
Solution 7 - JavaParag VaidyaView Answer on Stackoverflow
Solution 8 - JavaHimanshu AhireView Answer on Stackoverflow