javanullpointerexceptionjava-stream

What is causing this NullPointerException in Stream.anyMatch?


I have some code that is playing with the Java Stream API:

boolean isAuthorized = authorizationByTenant.stream()
                .filter(auth -> auth.getTenantName().equalsIgnoreCase(tenant))
                .map(auth -> auth.getAuthorizedRoutes().get(component))
                .flatMap(Collection::stream)
                .anyMatch(routeDefinition -> isMatchingRoute(routePath, routeDefinition));

And I sometimes get an exception pointing to the anyMatch line:

java.lang.NullPointerException: null
    at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na]
    at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1602) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
    at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) ~[na:na]
    at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:632) ~[na:na]
    at com.mycompany.routefilter.functionality.FunctionalityService.isAuthorizedRoute(FunctionalityService.java:55) ~[classes!/:1.0-SNAPSHOT]
    at

My question here is what is null? Is it routeDefinition ? And why does it make the call fail? I expect that if routeDefinition is null my method isMatchingRoute would return false.


Solution

  • The NullPointerException is occurring in your .flatMap(Collection::stream) expression.

    I created a similar example and expanded the method reference into a lambda. When causing a null value there you will see the stacktrace become more clear.

    Example:

    Stream.of("foo")
        .map(foo -> (List<String>) null)
        .flatMap(Collection::stream)
        .anyMatch(string -> "gnarly".equals(string)); // LINE 13
    

    Leads to

    Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
    at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
    at Test.main(Test.java:13)
    

    When I replace it with:

      Stream.of("foo")
            .map(foo -> (List<String>) null)
            .flatMap(strings -> strings.stream()) // LINE 11
            .anyMatch(string -> "gnarly".equals(string)); // LINE 12
    

    Then the exception becomes:

    Exception in thread "main" java.lang.NullPointerException
        at Test.lambda$main$1(Test.java:11)
        at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
        at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
        at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
        at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
        at Test.main(Test.java:12)
    

    Here you see at the top of the stack trace that the real cause is in line 11. A bit confusing indeed, but I guess the JRE cannot report exceptions occurring within method references with line number accuracy.

    EDIT: Regarding solutions, my IntelliJ suggests the following:

    IntelliJ suggestions

    The filter:

    .filter(Objects::nonNull)
    .flatMap(Collection::stream)
    

    The null safe lambda:

    .flatMap(strings -> strings != null ? strings.stream() : null)