As an example, here's the code for the latter (and most complicated) case:
/**
* Inverts a map. The map may also be a non-Guava multimap (i. e. the entrySet elements may have collections as values).
*
* @param map the map to inverted
* @param valueStreamer a function that converts an original map value to a stream. (In case of a multimap, may stream the original
* value collection.) Can also perform any other transformation on the original values to make them suitable as keys.
* @param mapFactory a factory which returns a new empty Map into which the results will be inserted
* @param collectionFactory a factory for the value type of the inverted map
* @return the inverted map, where all keys that map to the same value are now mapped from that value
*/
public static <K, V, V1, C extends Collection<K>> Map<V, C> invertMultiMap(Map<K, V1> map, Function<V1, Stream<V>> valueStreamer,
Supplier<Map<V, C>> mapFactory, Supplier<C> collectionFactory) {
Map<V, C> inverted = map.entrySet().stream().flatMap(e ->
valueStreamer.apply(e.getValue()).map(v -> new SimpleEntry<>(v, newCollection(e.getKey(), collectionFactory))))
.collect(toMap(Entry::getKey, Entry::getValue, (a, b) -> {a.addAll(b); return a;}, mapFactory));
return inverted;
}
private static <T, E extends T, C extends Collection<T>> C newCollection(E elem, Supplier<C> s) {
C collection = s.get();
collection.add(elem);
return collection;
}
You can find the complete code, covering some more cases and with some tests, in this Gist. The tests depend on Guava.