javajava-streamoptional-chaining

Adding Optionals/null safety to a nested flatMap/stream


I'm having trouble making my method null-safe using streams and flatMap..

My input is a List<RequestSlot>

And the objects are nested as such:

        Request
                OuterItem
                        List<InnerItem>

This is working to flatten the lists and create one List, but I'm unsure how to add Optionals here to make this null safe for each map/stream step.

  final List<InnerItem> innerItem = request.stream()
          .map(RequestSlot::getOuterItem)
          .flatMap(outerItem -> outerItem.getInnerItem().stream()).collect(Collectors.toList());

I have tried using Stream.ofNullable but that ends up creating a stream that I'm unsure how to then collect. Any pointers?


Solution

  • You don't actually need to use Optional. Just filter(Objects:nonNull) everywhere after mapping to something that might be null. This filters out all the null things

    // assuming request itself is not null
    final List<InnerItem> innerItem = request.stream()
        .filter(Objects:nonNull) // list elements might be null
        .map(RequestSlot::getOuterItem) 
        .filter(Objects:nonNull) // outer item might be null
        .map(OuterItem::getInnerItem)
        .filter(Objects:nonNull) // inner items list might be null
        .flatMap(Collection::stream) // innerItemList -> innerItemList.stream()
        .filter(Objects:nonNull) // inner item list might contain nulls
        .toList();
    

    If you really want to use Stream.ofNullable, use the pattern flatMap(x -> Stream.ofNullable(x.getX())) (where getX might return null).

    final List<InnerItem> innerItem = request.stream()
        .filter(Objects:nonNull) // list elements might be null
        .flatMap(requestSlot -> Stream.ofNullable(requestSlot.getOuterItem())) // outer item might be null
        .flatMap(outerItem -> Stream.ofNullable(outerItem.getInnerItem())) // inner item list might be null
        .flatMap(Collection::stream) // innerItemList -> innerItemList.stream()
        .filter(Objects:nonNull) // inner item list might contain nulls
        .toList();