I’m new to Streams and I want something like this:
I have an input Map with <String, List<String>>
. I want to read this map in a stream and assign it to an output map with <String, String>
with the value being the first element of the value list from input map.
Example:
**Input:** {key1, [value1 value2]} {key2, null} **Output** {key1, value1} {key2, null}
Notice, when the list in the first map is null, then it should be written as null in the second map. If the list is empty, then it should also write null in the second map value
What I have tried:
Map<String, String> output= input.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
This gives a java.lang.NullPointerException
when the list is empty in the first map.
Advertisement
Answer
Unfortunately, Collectors.toMap
will also throw if you put a null
value in.
To get around this, you can in-line build a Collector
for Map
. For example, something like:
final Map<String, String> output = input.entrySet().stream() .collect(HashMap::new, // Create a Map if none is present (map, entry) -> map.put(entry.getKey(), // Keys stay the same (entry.getValue() == null || entry.getValue().isEmpty()) // Check for empty ? null : entry.getValue().iterator().next()), // Get first if present HashMap::putAll); // Combining function
Note: wrapping this with Collections.unmodifiableMap
makes sense to avoid future contamination.
Note: Pulling out the ‘get first value or null’ to a method like the following is probably considerably more readable, and allows the combining bit to just become this.getFirstIfPresent(entry.getValue())
in the above pipeline:
private static <T> @Nullable T getFirstIfPresent(final List<T> input) { if (list == null || list.isEmpty()) { return null; } return list.iterator().next(); }