I am dealing with a Map<String,String>
that has null
entries in the key and/or value:
Map<String, String> headers = new HashMap<>();
headers.put("SomE", "GreETing");
headers.put("HELLO", null);
headers.put(null, "WOrLd");
headers.keySet().stream().forEach(k -> System.out.println(k + " => " + copy.get(k)));
I get the following output:
SomE => GreETing
HELLO => null
null => WOrLd
I need to transform the map, so all the non-null values are converted to lowercase, like so:
some => greeting
hello => null
null => world
I am trying to use Java 8 streams API, but the following code is throwing NullPointerException
:
Map<String,String> copy
= headers.entrySet()
.stream()
.collect(
Collectors.toMap(
it -> it.getKey() != null ? it.getKey().toLowerCase() : null,
it -> it.getValue() != null ? it.getValue().toLowerCase() : null));
copy.keySet().stream().forEach(k -> System.out.println(k + " => " + copy.get(k)));
If I comment out the last two map entries, the program executes, so there must be an issue with how Collectors.toMap
works when keys or values are null. How do I use the streams API to work around this?
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
);
}
Use Collect:
final Function<String, String> fn= str -> str == null ? null : str.toLowerCase();
Map<String, String> copy = headers.entrySet().stream()
.collect(HashMap::new,
(m, e) -> m.put(fn.apply(e.getKey()), fn.apply(e.getValue())),
Map::putAll);
Or with AbacusUtil
Map<String, String> copy = Stream.of(headers)
.collect(HashMap::new,
(m, e) -> m.put(N.toLowerCase(e.getKey()), N.toLowerCase(e.getValue())));
updated on 2/4, Or:
Map<String, String> copy = EntryStream.of(headers)
.toMap(entry -> N.toLowerCase(entry.getKey()), entry -> N.toLowerCase(entry.getValue()));
You cannot use Collectors.toMap()
without getting a NPE since you have a null
value
present in your map
, as explained by @dkatzel already, but I still wanted to use Stream API
;
Map<String, String> headers = new HashMap<>();
headers.put("good", "AsDf");
headers.put("SomE", "GreETing");
headers.put("HELLO", null);
headers.put(null, "WOrLd");
new HashSet<>(headers.entrySet()).stream()
.peek(entry -> entry.setValue(Objects.isNull(entry.getValue()) ? null : entry.getValue().toLowerCase()))
.filter(entry -> !Objects.isNull(entry.getKey()) && !entry.getKey().equals(entry.getKey().toLowerCase()))
.forEach(entry -> {
headers.put(entry.getKey().toLowerCase(), entry.getValue());
headers.remove(entry.getKey());
});
System.out.println(headers);
Prints out;
{null=world, some=greeting, hello=null, good=asdf}
If you have complicated checks and still want to use Collectors.toMap() without nulls
// convert data to a final key, value pairs using Pair.of(key, value)
.map(data -> {
SomeKey key = makeKey(data.getKey());
SomeVal val = makeValue(data.getValue());
// validate and return
if(key.foo != null && val.bar != null) {
return Pair.of(key.foo, val.bar);
}
return null;
})
.filter(Objects::nonNull) // remove the ones that don't satisy criteria
.collect(Collectors.toMap(pair -> pair.getLeft(), pair -> pair.getRight(), // then collect final key/value data without NPE checks
(left, right) -> {
left.merge(right); // can return null
return left;
}
)));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.