I made up this function, it seems to make the work, but I wondered if there was an even cleaner solution.
public static <K, V> Map<V, List<K>> reverseMap(Map<K, List<V>> map) {
return map.entrySet().stream()
.flatMap(entry -> entry.getValue().stream().map(value -> new AbstractMap.SimpleEntry<>(value, entry.getKey())))
.collect(Collectors.groupingBy(
AbstractMap.SimpleEntry::getKey,
Collectors.mapping(AbstractMap.SimpleEntry::getValue, Collectors.toList())));
}
Bonus question: I have a java 8 constraint for this one, but how could later versions improve it? I assume I’d no longer have to use AbstractMap.SimpleEntry since Java 9 introduced the Map.entry(k, v) function.
>Solution :
Since you did not necessitate streams in your question, I’m going to advocate for a non-stream solution:
//example input
Map<Integer, List<Integer>> map = new HashMap<>();
map.put(1, Arrays.asList(11, 12, 13, 4));
map.put(2, Arrays.asList(21, 22, 23, 4));
map.put(3, Arrays.asList(31, 32, 33, 4));
//reversing
Map<Integer, List<Integer>> reversed = new HashMap<>();
map.forEach((key, list) -> {
list.forEach(value -> {
reversed.computeIfAbsent(value, k -> new ArrayList<>()).add(key);
});
});
//end result:
//{32=[3], 33=[3], 4=[1, 2, 3], 21=[2], 22=[2], 23=[2], 11=[1], 12=[1], 13=[1], 31=[3]}
In your stream-based solution, you will be creating new Entry objects per key-(list-value) pair in the map, which will then have to make additional entries when recombined into a map. By using a direct approach, you avoid this excess object creation and directly create the entries you need.
Note that not everything has to be a Stream, the "old" way of doing things can still be correct, if not better (in readability and performance terms) than a Stream implementation.