Null check to an list before apply java steam to it

I have a below code

item.getSubCategory().stream().map(category -> {
              return subCategoriesList.add(new SubCategoryViewModel(
                      category.get(String.format("_%s",categoryProperties[0])).toString(),
                      category.get(categoryProperties[2]).toString(),
                      category.get(categoryProperties[3]).toString()));
            }).collect(Collectors.toList());

The item.getSubCategory() is a list of category model. Now if null appears on the getSubCategory we get an null pointer exception and can't apply steam to it. Is there a better way to handle null check to the list before applying stream to it.

I don't want to use IF statement to check null on getSubCategory. Is there any better way in java steam API ?

4 answers

  • answered 2021-02-24 08:37 Boug

    Is there a better way to handle null check to the list before applying stream to it.

    Only one way I can think of. Make item item.getSubCategory() to return Optional<YourObject> instead of the actual object.

    I don't want to use IF statement to check null on getSubCategory. Is there any better way in java steam API ?

    Your concern doesn't have to do with stream API. What you ask is how to treat an object that could be null, so that I don't have to use If, else checks.

  • answered 2021-02-24 08:43 Amongalen

    A method returning a list should never return null - it is a very bad practice. Just initialize the list in the constructor and return an empty one if there aren't any objects added to it. This way you eliminate all null checks that are needed whenever you want to use getSubCategory.

    One could say that you end up with an unused object that takes memory. However, unless you have really a lot of item objects, having a few "spare" lists won't hurt anyone. Remember that code readability comes first in most cases - fix performance when you see actual problems.

    Edit: as suggested by Holger, if you're worried about the memory cost of those empty lists, you could use Collections.emptyList(). Inside of your item you keep everything as it was, create the list only when it is needed and such. However, inside of getSubCategory instead of returning null you should check if the list is initialized and if it isn't then return Collections.emptyList() in place of null. According to documentation, Collections.emptyList() returns an immutable instance of a list, and multiple calls will return the same instance - only one will ever be created.

  • answered 2021-02-24 09:30 Lino

    Starting with Java 9, you can also make use of Objects.requireNonNullElse():

    Objects.requireNonNullElse(item.getSubCategory(), Collections.emptyList())
        .stream()
        .map(...);
    

    This may look similar to:

    Optional.ofNullable(item.getSubCategory()).orElse(Collections.emptyList())
        .stream()
        .map(...);
    

    But it is different in the way, that requireNonNullElse() will directly return a non null List, while the Optional variant wraps the initial List in an Optional and is then unwrapped with orElse()

  • answered 2021-02-24 13:53 ETO

    Perhaps this should be a good candidate for a comment, but I doubt one would be enough to explain my point.


    I see very often people suggesting empty lists (or any empty collections) instead of null. In my view those are two completely different result types: empty list and absent list. Maybe sometimes an empty list is just fine, but that is not necessarily true in all the cases.

    I agree that returning a null is a poor practice leading to lots of further issues. But null is not the only way of expressing absence of a result. I'd rather use Optional.empty() instead of an empty list as a default result.

    Here's an example demonstrating my point.

    Let's assume we have a method fetching list of consumption records from an electricity meter. Sometimes the device might be offline, so you can't fetch anything and will retry later. Now take a look at these two methods:

    List<ConsumptionAmount> getConsumptionData(Date from, Date to); 
    
    Optional<List<ConsumptionAmount>> getConsumptionData(Date from, Date to); 
    

    The first implementation is very tricky. Returning empty list is very confusing (hence saying there was no energy consumption at all).

    List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
    // report 'zero consumption' to the billing service
    

    Another option is to propagate an exception, which can also cause a try-catch mess in the code (The caller must be aware of the exception types and behave accordingly, otherwise the exception will be propagated further causing an even bigger mess):

    try {
        List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
        // report consumption to the billing service
    } catch (WhateverException ex) {
        // handle missing data here
    }
    

    The second implementation in contrast does not provide any details on the issue causing the absence of data, but it doesn't confuse the caller neither.

    getConsumptionData(from, to).ifPresent( list -> /* report consumption here */);
    

    If you do care of missing result, then simply provide another method ref:

    getConsumptionData(from, to).ifPresentOrElse( 
        list -> /* report consumption here    */,
        ()   -> /* handle missing data here */
    );
    

    There are even more interesting functional approaches to such use cases (Either and Try), but unfortunately Java doesn't provide them out of the box. You can check third party frameworks like this one