Nullpointerexception in Collectors.Tomap with Null Entry Values

NullPointerException in Collectors.toMap with null entry values

You can work around this known bug in OpenJDK with this:

Map<Integer, Boolean> collect = list.stream()
.collect(HashMap::new, (m,v)->m.put(v.getId(), v.getAnswer()), HashMap::putAll);

It is not that much pretty, but it works. Result:

1: true
2: true
3: null

(this tutorial helped me the most.)

EDIT:

Unlike Collectors.toMap, this will silently replace values if you have the same key multiple times, as @mmdemirbas pointed out in the comments. If you don't want this, look at the link in the comment.

Collecting values that could be null

You have incorrect syntax. toMap()'s second parameter must be lambda, so

.collect(Collectors.toMap(
pair -> pair.getKey(),
pair -> Optional.ofNullable(pair.getValue()).orElse("")
));

OR

you can modify map() section as follows

return Pair.of(name, Optional.ofNullable(pairValue).orElse(""));

and use your original collect()

java stream throws NPE when toMap stores a null value

Collector.toMap uses HashMap::merge to combine results:

public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException();
if (remappingFunction == null)
throw new NullPointerException();

So it may store null values, but merge does not allow it.

You can do a work around, by using forEach

stream.forEach (
r-> map.put(r.getName(), r.get(obj))
)

Collectors.toMap() giving NPE Internally

HashMap::merge throws if any value is null.

@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException(); //here

So getValueHere must return null. You only do a null check against the item and the key, never the value.

You can add another filter, or make sure your class doesn't contain null values.

.filter(it -> Objects.nonNull(it.getValueHere()))

Use Java 8 streams to transform a Map with nulls

The problem is toMap() invokes the underlying Map implementation being built's merge() function which does not allow values to be null

from the javadoc for Map#merge (emphasis mine)

If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null.

So using Collectors.toMap() will not work.

You can do this without stream just fine:

Map<String,String> copy = new HashMap<>();

for(Entry<String, String> entry : headers.entrySet()){
copy.put(entry.getKey() !=null ? entry.getKey().toLowerCase() : null,
entry.getValue() !=null ? entry.getValue().toLowerCase() : null
);
}

Collect to map skipping null key/values

If you want to avoid evaluating the functions func1 and func2 twice, you have to store the results. E.g.

stream.map(t -> new AbstractMap.SimpleImmutableEntry<>(func1(t), func2(t))
.filter(e -> e.getKey()!=null && e.getValue()!=null)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

This doesn’t make the code shorter and even the efficiency depends on the circumstances. This change pays off, if the costs of evaluating the func1 and func2 are high enough to compensate the creation of temporary objects. In principle, the temporary object could get optimized away, but this isn’t guaranteed.

Starting with Java 9, you can replace new AbstractMap.SimpleImmutableEntry<>(…) with Map.entry(…). Since this entry type disallows null right from the start, it would need filtering before constructing the entry:

stream.flatMap(t -> {
Type1 value1 = func1(t);
Type2 value2 = func2(t);
return value1!=null && value2!=null? Stream.of(Map.entry(value1, value2)): null;
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Alternatively, you may use a pair type of one of the libraries you’re already using (the Java API itself doesn’t offer such a type).

NullPointerException: element cannot be mapped to a null key

Use Collectors.toMap instead and specify that a HashMap is used (as it allows one null key)

 Collectors.toMap(
MappingEntry::getMilestone,
x -> {
List<MappingEntry> list = new ArrayList<>();
list.add(x);
return list;
},
(left, right) -> {
left.addAll(right);
return left;
},
HashMap::new

)

How to filter the Map with Stream when null occurs?

Optional is a container object which is used to contain not-null objects. Optional object is used to represent null with absent value.

You can do something like this:

Map<Integer, Optional<String>> map = new HashMap<>(); 

//Optional.ofNullable - allows passed parameter to be null.
map.put(1, Optional.ofNullable("one"));
map.put(2, Optional.ofNullable("two"));
map.put(3, Optional.ofNullable(null));
map.put(4, Optional.ofNullable("four"));

Map<Integer, Optional<String>> map2 = map.entrySet().stream()
.filter(e -> e.getKey() > 2)
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

System.out.println(map2.toString());

Test

{3=Optional.empty, 4=Optional[four]}

For more utility methods to facilitate code to handle values as available or not available instead of checking null values, I refer you to the documentation.



Related Topics



Leave a reply



Submit