7

In Java 7, if I want to get the last not null element of a list, I write something like this:

public CustomObject getLastObject(List<CustomObject> list) {
    for (int index = list.size() - 1; index > 0; index--) {
        if (list.get(index) != null) {
            return list.get(index);
        }
    }
    // handling of case when all elements are null
    // or list is empty
    ...
}

I want to write a shorter code by using lambdas or another feature of Java 8. For example, if I want to get the first not null element I can write this:

public void someMethod(List<CustomObject> list) {
    .....
    CustomObject object = getFirstObject(list).orElseGet(/*handle this case*/);
    .....
}

public Optional<CustomObject> getFirstObject(List<CustomObject> list) {
    return list.stream().filter(object -> object != null).findFirst();
}

Maybe someone know how to solve this problem?

sbeliakov
  • 2,169
  • 1
  • 20
  • 37
ZhenyaM
  • 676
  • 1
  • 5
  • 15
  • It is not **always** a good idea to use lambdas. Given the answer of @Tunaki it's clearer to the reader using the non-lambda way. – lschuetze Sep 18 '15 at 13:11
  • 1
    Related: [“How to get ordered stream from a list in reverse order in Java 8”](http://stackoverflow.com/q/29403614/2711488) and [“Most efficient way to get the last element of a stream”](http://stackoverflow.com/q/27547519/2711488) – Holger Sep 18 '15 at 13:13
  • @lschuetze, I just learn possibilities of lambdas and I wanna know how can I decide simple problems using new features – ZhenyaM Sep 18 '15 at 13:22

1 Answers1

12

A possible solution would be to iterate over the List in reverse order and keep the first non null element:

public Optional<CustomObject> getLastObject(List<CustomObject> list) {
    return IntStream.range(0, list.size()).mapToObj(i -> list.get(list.size() - i - 1))
                                          .filter(Objects::nonNull)
                                          .findFirst();
}

Note that there is no findLast method in the Stream API because a Stream is not necessarily ordered or finite.

Another solution is to iterate over the list and reduce it by keeping only the current element. This effectively reduces the Stream to the last element.

public Optional<CustomObject> getLastObject(List<CustomObject> list) {
    return list.stream().filter(Objects::nonNull).reduce((a, b) -> b);
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 2
    @assylias The stream returned by [`Collection.stream()`](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#stream--) is sequential (per the Javadoc) – Tunaki Sep 18 '15 at 13:17
  • 1
    That’s why I linked to the other questions, to show that, e.g. using `reduce` to get the last element [is an established solution](http://stackoverflow.com/a/27547525/2711488). It even works in parallel execution. The reduction function must be associative but doesn’t need to be commutative. – Holger Sep 18 '15 at 13:33