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
How to Make a Java Thread Wait for Another Thread's Output
Which Cipher Suites to Enable for Ssl Socket
How to Get the Size of a Java.Sql.Resultset
Rotating Coordinate Plane for Data and Text in Java
Copying Files from One Directory to Another in Java
How to Use Regular Expressions to Parse HTML in Java
What Is the Use of Interface Constants
What Does Swingutilities.Invokelater Do
How to Resize an Image Using Java
How to Use Cardlayout with Netbeans Gui Builder
When Do Java Generics Require <? Extends T> Instead of <T> and Is There Any Downside of Switching
How to Save the State of My Program and Then Load It
Junit 5: How to Assert an Exception Is Thrown
Individual and Not Continuous Jtable's Cell Selection
Why Is the Clone() Method Protected in Java.Lang.Object
How to Use an Internet Time Server to Get the Time