NullPointerException in Collectors.toMap with null entry values
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Collectors.toMap throws a NullPointerException when the value-mapping function returns null. This surprises most developers because HashMap.put accepts null values without complaint. The root cause is that toMap internally calls Map.merge, which explicitly rejects null values. The fix depends on what you want: filter nulls out, substitute a default, or use a manual collector that permits nulls.
Why toMap Rejects Null Values
The two-argument form of Collectors.toMap looks straightforward:
But if any person's email is null, this throws:
The stack trace reveals the mechanism: Collectors.toMap calls Map.merge under the hood. The merge method's contract explicitly requires non-null values because it needs to distinguish between "this key maps to null" and "this key is absent" when applying the merge function.
Here is a minimal reproduction:
Solution 1: Filter Out Null Values
When entries with null values should simply be excluded from the result, filtering is the cleanest and most readable approach:
Result: {[email protected], [email protected]}
Bob is excluded entirely. This is appropriate when downstream code treats a missing key the same as a null value, which is the case in most map lookups.
You can chain multiple filters when both keys and values might be null:
Solution 2: Substitute a Default Value
When the resulting map must contain every key, replace nulls with a sentinel value before collecting:
Result: {[email protected], Bob=, [email protected]}
Choose the default carefully. An empty string, a domain-specific placeholder like "not_provided", or Optional.empty() (if you switch to Map<String, Optional<String>>) each communicate different things to downstream consumers.
Objects.requireNonNullElse (Java 9+) is a concise alternative to the ternary expression.
Solution 3: Manual Collector for Null-Permissive Maps
When you genuinely need a map that stores null values (not a sentinel, but actual null), Collectors.toMap is the wrong tool. Use the three-argument collect method with explicit accumulation:
Result: {[email protected], Bob=null, [email protected]}
This works because HashMap.put accepts null values. The three arguments are:
- Supplier: Creates the result container (
HashMap::new). - Accumulator: Adds each element to the container.
- Combiner: Merges two containers during parallel execution.
Extracting a reusable utility
If this pattern appears frequently in your codebase, extract it:
Handling Duplicate Keys: A Separate Problem
Even after fixing the null-value issue, toMap can throw IllegalStateException if two elements produce the same key:
The three-argument form of toMap adds a merge function to resolve conflicts:
Common merge strategies:
Null handling and duplicate-key handling are independent concerns. You may need to address both in the same stream pipeline, but they require different fixes.
toMap vs groupingBy: When to Switch
If multiple elements can share the same key and you want to keep all values, Collectors.groupingBy is often a better fit than toMap with a merge function:
groupingBy never throws on duplicate keys because it expects multiple values per key. It also handles null values in the grouped list without issues (the list can contain nulls).
Comparison of Approaches
| Approach | Null values in result? | Duplicate key handling | Readability |
filter + toMap | No (excluded) | Throws by default | High |
Default substitution + toMap | No (replaced) | Throws by default | High |
Manual collect with HashMap | Yes | Last writer wins | Medium |
Custom toNullableMap utility | Yes | Configurable | High (after initial definition) |
groupingBy | Yes (in lists) | Built-in (groups) | High |
Common Pitfalls
Assuming Collectors.toMap behaves like HashMap.put. The most common surprise. toMap uses Map.merge internally, which rejects null values. HashMap.put does not share this restriction.
Fixing null values but forgetting about duplicate keys. These are independent failure modes. Filtering nulls does not prevent IllegalStateException from duplicates, and a merge function does not prevent NullPointerException from null values.
Using a sentinel value that looks like real data. Substituting "[email protected]" as a default is dangerous if downstream code might send emails to that address. Use clearly synthetic values like empty strings or dedicated constants.
Using the manual collector without considering parallel stream safety. The HashMap::putAll combiner in the manual approach has "last writer wins" semantics. If two parallel partitions contain the same key, one value is silently lost. If that is unacceptable, add conflict detection in the combiner.
Ignoring that toUnmodifiableMap (Java 10+) also rejects null values. Switching from toMap to toUnmodifiableMap does not solve the problem. Both use merge internally and both throw on null values.
Over-engineering with Optional. Wrapping every value in Optional<String> to avoid null creates a map that is verbose to consume. Prefer filtering or defaults over Map<K, Optional<V>> unless you have a specific reason to distinguish "present but empty" from "absent."
Summary
Collectors.toMapthrowsNullPointerExceptionon null values because it usesMap.mergeinternally, notMap.put.- Filter null values before collecting when missing entries are acceptable.
- Substitute a default value (using
Objects.requireNonNullElseor a ternary) when every key must appear in the result. - Use the three-argument
collectmethod withHashMap::newwhen the map genuinely needs to store null values. - Handle duplicate keys separately with a merge function in the three-argument
toMapoverload. - Consider
groupingByinstead oftoMapwhen multiple elements naturally share the same key.
Related reading
- NullPointerException in Java with no StackTrace
- NullPointerException in Junit 5 MockBean
- Number of days between two dates in Joda-Time
- Number of lines in a file in Java
- NullPointerException when trying to access views in a Kotlin fragment
- Numpy is installed but still getting error
- object kafka is not a member of package org.apache
- Object not serializable (org.apache.kafka.clients.consumer.ConsumerRecord) in Java spark kafka streaming

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.