0 votes
1 view
in Java by (3.2k points)

The Java 8 Collectors.toMap throws a NullPointerException if one of the values is 'null'. I don't understand this behaviour, maps can contain null pointers as value without any problems. Is there a good reason why values cannot be null for Collectors.toMap?

Also, is there a nice Java 8 way of fixing this, or should I revert to plain old for loop?

An example of my problem:

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() {

        return id;

    }

    public void setId(int id) {

        this.id = id;

    }

    public Boolean getAnswer() {

        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));

    }

}

Stacktrace:

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)

This problem still exists in Java 11.

1 Answer

0 votes
by (27.9k points)

You can run throughout this known bug in OpenJDK amidst this:

Map<Integer, Boolean> collect = list.stream()

        .collect(HashMap::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap::putAll);

It is not that large, but it operates. Result:

1: true

2: true

3: null

(For more you can refer to this tutorial.)

...