javaexceptioncollectionsnullpointerexception

Why list.contains(null) throwing null pointer exception?


I have List<Long> countriesList which contains Long values which are country ids.

Now I am iterating over some List<UserRequests> list by using streams.

userRequests.stream().
    forEach(userRequest->
    {
        UserData=userRepository.findById(userRequest.getId()); //fine
        if (CollectionUtils.isNotEmpty(countriesList) && countriesList.contains(userRequest.getCountryId()))//getting NPE here
        {
            //do some operation
        }
    });

I tried to debug by evaluating individual statements. I have made sure countriesList have some data First part CollectionUtils.isNotEmpty(countriesList) is returning true. userRequest is also not null but userRequest.getCountryId() is null. And when I evaluated countriesList.contains(userRequest.getCountryId()), I am getting Null pointer exception here. why not false ? I am confused what wrong am I doing. Is default list.contains(null) behaviour is like that only or is it because I am calling it within stream()?

Just for simplification I have created simple list and compared with null.

class Test {
    public static void main(String[] args) {
        List<Long> longList = List.of(1L, 2L, 3L);
        System.out.println("Comparing long list with null::" + longList.contains(null));
    }
}

This is the exception I am getting:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:222)
    at java.base/java.util.ImmutableCollections$AbstractImmutableList.indexOf(ImmutableCollections.java:166)
    at java.base/java.util.ImmutableCollections$AbstractImmutableList.contains(ImmutableCollections.java:197)
    at com.test.Test.main(Test.java:26)

But If I do:

    List<Long> longList = new ArrayList<>();
    longList.add(1L);
    System.out.println("Comparing long list with null::" + longList.contains(null));

Why it is printing false? Why no NullPointerException here?


Solution

  • Why list.contains(null) throwing null pointer exception?

    Because this is what the specs say can happen.

    The specific reasoning for the List.of(...) case is as follows:

    1. The List.of methods are specified as producing unmodifiable lists.

    2. Unmodifiable lists are specified to not permit null elements:

      "They disallow null elements. Attempts to create them with null elements result in NullPointerException."

    3. The javadoc for List.contains states:

      "Throws: NullPointerException - if the specified element is null and this list does not permit null elements.

    In other words, what you are seeing is specified behavior ... and not a result of an incorrect or capricious implementation.

    For the case where you are searching countryList ... it will depend on the actual List implementation class. But there is a clear expectation in the List spec for contains that some kinds of list may throw an NPE if you try to search for null.

    (Whether that is a "poor design choice" or not is a matter of opinion. I don't think so. But either way, this is a moot point. The List design choices were made a long time ago and changing them would now would be too disruptive to contemplate. And I doubt that you would convince the current Java design team that allowing null values in collection types is a good idea.)

    Note: the same restriction applies to immutable sets; e.g. as created using Set.of(...) calls.