1. Overview

The Java Stream API was the major feature of the Java 8 release. Streams represent lazily-evaluated sequences of objects and provide a rich, fluent, and monadic-like API.

In this article, we will have a quick look into ways of getting the last element of a Stream. Keep in mind that due to the nature of streams, it's not a natural operation. Always make sure that you're not working with infinite Streams.

2. Using the Reduce API

Reduce, simply put, reduces the set of elements in a Stream to a single element.

In this case, we'll reduce the set of elements to get the last element fn the Stream. Keep in mind that this method will only return deterministic results for sequential Streams.

Let's use a List of String values, get the Stream from the List and then reduce:

List<String> valueList = new ArrayList<>();

Stream<String> stream = valueList.stream();
stream.reduce((first, second) -> second)

Here, the stream is reduced to a level where it is left with only the last element. If the stream is empty it will return a null value.

2. Using the Skip Function

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.

Let's create a List of string values and use its size function to determine how many elements to skip to reach the last element.

Here is the example code getting the last element using skip:

List<String> valueList = new ArrayList<String>();

long count = valueList.stream().count();
Stream<String> stream = valueList.stream();
stream.skip(count - 1).findFirst().get();

“Sean” ends up being the last element.

4. Getting the Last Element of an Infinite Stream

Trying to get the last element of the infinite stream would lead to an infinite sequence of evaluation performed on infinite elements. Both skip and reduce will not come back from the execution of evaluation unless we limit the infinite stream to a specific number of elements using limit operation.

Here is the example code where we took an infinite stream and tried to get the last element:

Stream<Integer> stream = Stream.iterate(0, i -> i + 1);
stream.reduce((first, second) -> second).orElse(null);

Consequently, the stream will not come back from the evaluation and it will end up halting the execution of the program.

5. Conclusion

We saw different ways of getting the last element of a Stream both using reduce and Skip APIs. We also looked into why this is not possible with infinite streams.

We saw that getting the last element from a Stream is not easy in comparison to fetching it from other data structures. This is because of the lazy nature of Streams which are not evaluated unless the terminal function is invoked and we never know if the currently evaluated element is the last one.

Code snippets can be found over on GitHub.