2

I am putting the results of a Java 11 stream into a Collectors.toMap(keyMapper, valueMapper) collector. It so happens that my value-mapper (lambda) returns a null value, resulting in a NullPointerException, which is surprising.

java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:221)
    at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:178)
    at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.base/java.util.TreeMap$EntrySpliterator.forEachRemaining(TreeMap.java:2962)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at [my code]

The fatal call to Objects.requireNonNull is in Collectors.uniqKeysMapAccumulator():

    private static <T, K, V>
    BiConsumer<Map<K, V>, T> uniqKeysMapAccumulator(Function<? super T, ? extends K> keyMapper,
                                                    Function<? super T, ? extends V> valueMapper) {
        return (map, element) -> {
            K k = keyMapper.apply(element);
            V v = Objects.requireNonNull(valueMapper.apply(element));
            V u = map.putIfAbsent(k, v);
            if (u != null) throw duplicateKeyException(k, u, v);
        };
    }

The crucial expression being Objects.requireNonNull(valueMapper.apply(element)).

Why is the collector requiring that map values are non null? Is this a bug in the implementation?


The Map.merge operation is required to reject null values. The documentation for the Collectors.toMap() methods that are given a mergeFunction state that it will be used by Map.merge. However, the Collectors documentation for the 2-argument toMap() methods (which do not have a mergeFunction) does not state that values must be non-null, nor does it state that Map.merge is invloved at all.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • @Oleksandr I thought so too, but this asks *why*, not how to overcome, not sure... – Eugene Oct 07 '18 at 19:06
  • @Oleksandr not really, the dupe has hints on why, but it's not like Stuart Marks came to answer that and said : this is why... – Eugene Oct 07 '18 at 19:11
  • @Eugene Found some answer from Stuart Marks, in the [bug](https://bugs.openjdk.java.net/browse/JDK-8148463) report comments :) – Oleksandr Pyrohov Oct 07 '18 at 19:18
  • 1
    @Oleksandr judging by that comment, it sure looks like they simply missed that point, thank you for the bug link! – Eugene Oct 07 '18 at 20:27
  • 2
    @Oleksandr well, except that this explanation does not apply to the current Java version, as it doesn’t use `Map.merge` (clearly visible in the stack trace). But never mind, I added another helpful Q&A to the list of duplicates to complete the picture. – Holger Oct 08 '18 at 08:15
  • @Eugene they missed that point in Java 8, but deliberately kept the behavior starting with Java 9. Actually, [you’ve seen this before](https://stackoverflow.com/a/49004223/2711488)… – Holger Oct 08 '18 at 08:17
  • 1
    @Holger ha! thank you :) – Eugene Oct 08 '18 at 08:19
  • @Holgar The question about `Map.merge` is related, but not a duplicate target. I've amended the question to clarify that and to include a link in the body. But this is still a duplicate question. – Raedwald Oct 08 '18 at 11:14

0 Answers0