0

I have a List of objects and want to get the last element from List for which property "personStatus" value is not null.

List<Person> personList = getAllPersons();
List<Person> personActive = personList.stream().filter(person -> person.getPersonStatus()!=null);

I tried the above code which gives List of Person whose status is not null, but i want to get the last Person object with status not null.

user7833845
  • 83
  • 2
  • 11

2 Answers2

0

You solved 80%: first collect and filter. This will give a new collection using Java 8 streams.

Literally: last

Then pick the last from the filtered collection, e.g. on list using list.get(lastIndex) where int lastIndex = list.size()-1 .

The last is a bit ambiguous, so I have 2 approaches ️

List<Person> personList = getAllPersons();
// typo: name of List should be plural e.g. `personActiveList` or `activePersons`
List<Person> personActive = personList.stream()
    .filter(person -> person.getPersonStatus()!=null)
    .toList();

// the last in order of appearance
Person lastInActiveList = personActice.get(personActice.size()-1); // may throw NPE if list empty


// the last who was active by some Instant timestamp
List<Person> sortByLastActive = personList.stream()
    .filter(person -> person.getPersonStatus()!=null)
    .sorted(Person::getTimestamp())  # sort by Instant property
    .toList();

Person lastRecentlyActice = sortByLastActive.get(sortByLastActive.size()-1); // may throw NPE if list empty

Note: For brevity I used toList() as terminator (since Java 16).

You could also use collect(Collectors.toList()) from older Java versions. Beware: Differences of Java 16's Stream.toList() and Stream.collect(Collectors.toList())?

⚠️ Caution: all random get operations on list can throw a NullPointerException (NPE) if the list is empty. This should be tested using if (list.isEmpty()) before to prevent illegal access.

Trick: reverse to first

The trick here as commented by Boris is to reverse the list before opening the stream, thus before it becomes filtered/collected as result. Afterwards you can directly pick the first.

List<Person> personActiveReversed = Collections.reverse(personList).stream()  // reverse to get last elements first
    .filter(person -> person.getPersonStatus()!=null)
    .toList();

// may throw NPE if list empty
Person last = personActiveReversed.get(0);

// safer: directly on stream
Optional<Person> last = Collections.reverse(personList).stream()  // reverse before streaming
    .filter(person -> person.getPersonStatus()!=null)
    .findFirst();  // originally the last or empty 

// evaluate optional
if (last.isPresent()) {
   System.out.println("found the last: " + last.get());
} else {
   System.out.println("nobody found at last :(");
}

Explained:

  • see comments by Boris below and on question: use Collections.reverse before .stream to reverse an ordered list (from last to first). Then reversedList.get(0) will get the originally last element.

  • findFirst() is the new terminator, returning Java 8 type-wrapper Optional. This is null-safe but requires a further test or convenience-methods like orElse(defaultObject), e.g. last.orElse(null).

See also

hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • 2
    Your comparator is invalid, it needs to be symmetric - otherwise sorting behaviour is undefined and can throw and error. – Boris the Spider Sep 28 '21 at 06:50
  • 3
    This has nothing to do with “sequential”. The broken comparator happens to provide the intended result due to implementation details of the *sorting algorithm* used behind the scenes. Since this sorting algorithm is intentionally unspecified, this comparator may produce unintended results in a different implementation or fail with an exception, regardless of whether the Stream is sequential or not. A well known example of the past were broken comparators failing with exceptions when Java 7 was introduced. Some occurrences might be [here](https://stackoverflow.com/questions/linked/11441666)… – Holger Sep 28 '21 at 10:23
-1
int lastNotNull = -1;
for(int i = 0; i < personList.size();i++){
    if(person.getPersonStatus()!=null)
        lastNotNull = i;
}

lastNotNull is the index of the last not null Person