This article is participating in “Java Theme Month – Java Debug Notes Event”, see < Event link > for more details.

Question: How do I handle a null pointer exception in Collectors. ToMap with a null value?

Collectors. ToMap If one of the values is null, a null pointer exception will be thrown. I don’t understand this behavior, because the map itself can contain null Pointers as values without any problems. Is there a good reason why, unlike the Map class, Collectors. ToMap does not allow null values?

Also, does Java 8 have a solution to this problem, or should it be turned into a normal for loop?

Here’s an example:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


class Answer {
    private int id;

    private Boolean answer;

    Answer() {
    }

    Answer(int id, Boolean answer) {
        this.id = id;
        this.answer = answer;
    }

    public int getId(a) {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boolean getAnswer(a) {
        return answer;
    }

    public void setAnswer(Boolean answer) {
        this.answer = answer; }}public class Main {
    public static void main(String[] args) {
        List<Answer> answerList = new ArrayList<>();

        answerList.add(new Answer(1.true));
        answerList.add(new Answer(2.true));
        answerList.add(new Answer(3.null)); Map<Integer, Boolean> answerMap = answerList .stream() .collect(Collectors.toMap(Answer::getId, Answer::getAnswer)); }}Copy the code

Stack trace error message:

Exception in thread "main" java.lang.NullPointerException
    at java.util.HashMap.merge(HashMap.java:1216)
    at java.util.stream.Collectors.lambda$toMap$168(Collectors.java:1320)
    at java.util.stream.Collectors$$Lambda$5/1528902577.accept(Unknown Source)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1359)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at Main.main(Main.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Copy the code

Answer 1:

It is impossible to avoid this problem using the Collectors static method toMap, and Javadoc explicitly explains that toMap relies on map. Merge.

Map.merge is described in doc as follows:

If the specified key isnullThrows a null pointer exception and is not supported by this functionnull key nullThe value.Copy the code

You can do this with a foreach

Map<Integer,  Boolean> answerMap = new HashMap<>();
answerList.forEach((answer) -> answerMap.put(answer.getId(), answer.getAnswer()));
Copy the code