How to handle nullable lists using java 8?

Viewed 15856

I'm making a service call and trying to handle response. Response might have a list of something. That list might be null.

Moreover, if list not null or not empty, then it needs to be filtered. In the code "entry" reference might be null if filtering gives nothing or response list is empty or null.

Currently i'm getting NPE when i try to use stream() on a null response list. How can i handle this situation?

@Getter
public class ServiceResponse {  
    List<ResponseEntry> entryList;
}

@Getter
public class ResponseEntry {
    String value;
}


ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry = serviceResponse.getEntryList()
    .stream()
    .filter(e -> "expectedValue".equals(e.getValue()))
    .findFirst()
    .orElse(null);

if (entry == null) { ... }

5 Answers

Stream.ofNullable (Java-9)

Returns a sequential Stream containing a single element, if non-null, otherwise returns an empty Stream.

Current Code

ResponseEntry entry = serviceResponse.getEntryList() // List<ResponseEntry>
        .stream() // NPE here   // Stream<ResponseEntry>
        .filter(e -> "expectedValue".equals(e.getValue())) // filter
        .findFirst() // Optional<ResponseEntry>
        .orElse(null); // or else null

Updated Code

ResponseEntry entry = Stream.ofNullable(serviceResponse.getEntryList()) // Stream<List<ResponseEntry>>
        .flatMap(List::stream) // Stream<ResponseEntry>
        .filter(e -> "expectedValue".equals(e.getValue())) // filter here
        .findFirst() // Optional<ResponseEntry>
        .orElse(null); // or else null

Optional.stream (Java-9)

returns a sequential Stream containing only that value, otherwise returns an empty Stream.

ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
        .stream() // Stream<List<ResponseEntry>>
        .flatMap(List::stream) // Stream<ResponseEntry>
        .filter(e -> "expectedValue".equals(e.getValue())) // filter here
        .findFirst() // Optional<ResponseEntry>
        .orElse(null); // or else null

Optional.isEmpty(Java-11)

If a value is not present, returns true, otherwise false

Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()) // Optional<List<ResponseEntry>>
        .orElseGet(Collections::emptyList) // or else empty List
        .stream() // Stream<ResponseEntry>
        .filter(e -> "expectedValue".equals(e.getValue())) // filter
        .findFirst(); // Optional<ResponseEntry>

if (entry.isEmpty()) { // !entry.isPresent in java-8
   // Do your work here
}

if list not null or not empty, then it needs to be filtered.

No need for Optional here, as it's not intended to replace simple if checks.

ResponseEntry entry = null;
List<ResponseEntry> responseEntries = serviceResponse.getEntryList();
if(responseEntries != null && !responseEntries.isEmpty()){
    entry = responseEntries.stream()
                .filter(e -> "expectedValue".equals(e.getValue()))
                .findFirst()
                .orElse(null);
}
  • reads "if responseEntries is not null and responseEntries is not empty then apply the filter operation and find the first item or else null". Very readable.

On the other hand, the optional approach:

ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
                              .orElseGet(() -> Collections.emptyList())
                              .stream()
                              .filter(e -> "expectedValue".equals(e.getValue()))
                              .findFirst();

if(!entry.isPresent()){ ... } // or entry.ifPresent(e -> ...) depending on the logic you're performing inside the block
  • unnecessarily creates objects that could be avoided and not really the intention of optional to be used as a substitute for simple "if" checks.

In Java 9, you could use the new method Objects.requireNonNullElse(T,T):

Objects.requireNonNullElse(serviceResponse.getEntryList(),
                           Collections.emptyList())

Apache Commons Collections actually has a method ListUtils.emptyIfNull(List<T>) which returns an empty list if the argument list is null. That's even better, but Objects.requireNonNullElse is the closest thing to it in Java SE.

If you're restricted to just Java 8, then I agree with Aomine's answer that trying to do something like go through Optional is worse than an if statement.

You could simply use the ternary operator:

ServiceResponse serviceResponse = service.getServiceResponse();
List<ResponseEntry> list = serviceResponse.getEntryList();

ResponseEntry entry = (list == null ? Collections.emptyList() : list)
    .stream()
    .filter(e -> "expectedValue".equals(e.getValue()))
    .findFirst()
    .orElse(null);

if (entry == null) { ... }

Sometimes, traditional is better IMO.

Another option would be to use the Optional monad:

Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()).flatMap(list ->
    list.stream().filter(e -> "expectedValue".equals(e.getValue())).findFirst()
);

if (!entry.isPresent()) {
    …
}

You might even use orElseGet instead of that if statement if your objective is to build (and return) a value, instead of executing a side effect.

Related