4

I have a map like bellow,

 [key = "car", value = ["bmw", "toyota"]]
 [key = "bike", value = ["honda", "kawasaki"]]

I want to convert it to another map using java 8 functional apis like bellow,

 [key = "bmw", value = "car"]
 [key = "toyota", value = "car"]
 [key = "honda", value = "bike"]
 [key = "kawasaki", value = "bike"]
7
  • Did you try inverting then without functional APIs first? Commented Nov 10, 2017 at 1:57
  • Doing it using iteration is not a problem, want to do it with functional apis Commented Nov 10, 2017 at 2:01
  • 2
    What if there are duplicate values? Commented Nov 10, 2017 at 2:04
  • 1
    @shmosel lets assume there will be no duplicate values. I just wanted to check how complicated it becomes with functional apis Commented Nov 10, 2017 at 2:32
  • 2
    @nullpointer: you don’t need to store the key list into a local variable: for(String k: map.keySet()) for(String key: map.get(k)) result.put(key, k);, but in either case, it’s doing an unnecessary lookup on the map it’s iterating over, so for(Map.Entry<String,List<String>> e: map.entrySet()) for(String key: e.getValue()) result.put(key, e.getKey()); is preferable… Commented Nov 10, 2017 at 9:13

2 Answers 2

8

Flatten the map values to entries then collect them:

Map<String, String> m2 = map
    .entrySet()
    .stream()
    .flatMap(e -> e.getValue().stream().map(v -> new AbstractMap.SimpleEntry<>(v, e.getKey())))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

This can be shortened by importing AbstractMap.SimpleEntry and Map.Entry.

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

3 Comments

You can shorten the final line by using Map.Entry (or just Entry) instead of AbstractMap.SimpleEntry.
@teppic in 9 there is Map.entry method to use instead of a Pair or AbstractMap.SimpleEntry
@Eugene: but everyone should be aware that they are not identical, i.e. Map.entry does not allow null, neither for key nor for value. If that is understood and not a problem for the actual use case, it might be the preferable option.
2

A solution that doesn’t need constructing temporary Map.Entry instances, is:

Map<String, String> result = source.entrySet().stream()
  .collect(HashMap::new, (m,e)->e.getValue().forEach(k->m.put(k,e.getKey())), Map::putAll);

You might notice the similarity to the non-stream solution

Map<String, String> result = new HashMap<>();
source.forEach((key, value) -> value.forEach(k -> result.put(k, key)));

or the pre-Java 8 solution

Map<String, String> result = new HashMap<>();
for(Map.Entry<String,List<String>> e: source.entrySet())
    for(String key: e.getValue()) result.put(key, e.getKey());

Comments

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.