0

I have a utility method defined as below.

public static Map<String, Map<String, String>> convertRawMapToStringValues(Map<String, Map<String, Object>> cassandraRowsRawMap) {

    Map<String, Map<String, String>> cassandraStrValuesMap = cassandraRowsRawMap.entrySet()
       .stream()
       .collect(Collectors.toMap(s -> s.getKey(),
          s -> s.getValue().entrySet().stream()
             .collect(Collectors.toMap(e -> e.getKey(), 
                                            e -> String.valueOf(e.getValue())))));
    return cassandraStrValuesMap;
}

The String.valueOf(e.getValue()) returns a "null" value from the call. I would like to get the null value for the string.

When I tried the below code, I get an NPE on first .collect call.

Map<String, Map<String, String>> cassandraStrValuesMap = cassandraRowsRawMap.entrySet()
   .stream()
   .collect(Collectors.toMap(s -> s.getKey(),
                             s -> s.getValue().entrySet().stream()
                      .collect(Collectors.toMap(e -> e.getKey(), 
                                                e -> e.getValue() == null ? null : String.valueOf(e.getValue())))));
    return cassandraStrValuesMap;
}
4
  • toMap does not support null values. See this question. Commented Jul 22, 2019 at 15:18
  • @Holger - Thanks. So, would the only other option be loop through the nested maps and update the values ? Commented Jul 22, 2019 at 15:24
  • Can you provide a sample input for your method? Commented Jul 22, 2019 at 15:27
  • You may adapt the collector of this answer. Commented Jul 22, 2019 at 15:27

1 Answer 1

1

The toMap collector doesn’t support null values. But it doesn’t always have to be the Stream API:

public static <K,T,R> Map<K,R> changeValues(
       Map<? extends K, T> in, Function<? super T, ? extends R> f) {

    Map<K,R> result = new HashMap<>(in.size());
    in.forEach((k,t) -> result.put(k, f.apply(t)));
    return result;
}

public static Map<String, Map<String, String>> convertRawMapToStringValues(
       Map<String, Map<String, Object>> in) {

    return changeValues(in, inner -> changeValues(inner, v -> v==null? null: v.toString()));
}

The utility method returns a map with the same keys and transformed values and is flexible enough to allow a recursive application to do the inner map transformation.


Alternatively, we may adapt the solution of this answer to

public static Map<String, Map<String, String>> convertRawMapToStringValues(
       Map<String, Map<String, Object>> in) {

    return in.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey,
            e -> e.getValue().entrySet().stream()
            .collect(
                HashMap::new,
                (m,e2) -> m.put(e2.getKey(),
                    e2.getValue() == null? null: e2.getValue().toString()),
                Map::putAll)));
}

Unlike the original toMap collector, this won’t throw on duplicate keys, but for this specific case where the input is already a Map, there shouldn’t be duplicate keys anyway.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, @holger. Both the approaches worked. I liked the utility method approach. It is helping me figure out a better way of writing code where I do not have to do multiple nested loops but don't have to be affected by limitations of the streams.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.