Java 8 NullPointerException Collectionneurs.toMap

La Java 8 Collectors.toMap jette un NullPointerException si une des valeurs est 'null'. Je ne comprends pas ce comportement, les cartes peuvent contenir des pointeurs null comme valeur sans aucun problème. Est-il une bonne raison pourquoi les valeurs ne peuvent pas être null pour Collectors.toMap?

Aussi, est-il un gentil Java 8 chemin de la fixation de ce, ou devrais-je revenir à la plaine de vieux pour la boucle?

Un exemple de mon problème:

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)

Ce problème existe toujours dans Java 11.

null a toujours été un peu problématique, comme dans TreeMap. Peut-être un bon moment pour essayer Optional<Boolean>? Sinon, split et utiliser le filtre.
null pourrait être un problème pour la clé, mais dans ce cas c'est la valeur.
Pas toutes les cartes ont des problèmes avec null, HashMap, par exemple, peut avoir un null clé et le nombre de null valeurs, vous pouvez essayer de créer un personnalisé Collector à l'aide d'un HashMap au lieu d'utiliser celui par défaut.
Mais l'implémentation par défaut est HashMap - comme indiqué dans la première ligne de stacktrace. Le problème n'est pas qu'un Map ne pouvez pas tenir null valeur, mais que le deuxième argument de Map#merge fonction ne peut pas être null.
Personnellement, vu les circonstances, je voudrais aller avec les flux de solution, ou forEach() si l'entrée est parallèle. Le joli petit cours d'eau en fonction des solutions ci-dessous pourraient avoir une terrible de la performance.

OriginalL'auteur Jasper | 2014-07-08