问题
I have a Map object Map<t1, Set<t2>>
, and I want to go into the set and turn t2
in the sets into the keys of the new map. The original key t1
will be the new value of the map.
For example, given a map containing two entries
{key1: [a, b, c], key2: [c, d]}
The resulting map would be
{a: [key1], b: [key1], c: [key1, key2], d: [key2]}
[ ] denotes Set in the above examples.
回答1:
Java 8:
map.entrySet()
.stream()
.flatMap(e -> e.getValue()
.stream()
.map(v -> new SimpleEntry<>(v, e.getKey())))
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toSet())))
Guava:
Multimaps.asMap(map.entrySet()
.stream()
.collect(ImmutableSetMultimap.flatteningToImmutableSetMultimap(
Entry::getKey, e -> e.getValue().stream()))
.inverse())
StreamEx:
EntryStream.of(map)
.flatMapValues(Set::stream)
.invert()
.grouping(Collectors.toSet())
回答2:
One way could be :
private static <T1,T2> Map<T1, Set<T2>> invertMap(Map<T2, Set<T1>> data) {
Map<T1, Set<T2>> output = data.entrySet().stream().collect(() -> new HashMap<T1, Set<T2>>(),
(mapLeft, leftEntry) -> {
for (T1 i : leftEntry.getValue()) {
Set<T2> values = mapLeft.get(i);
if (values == null)
values = new HashSet<>();
values.add(leftEntry.getKey());
mapLeft.put(i, values);
}
}, (mapLeft, mapRight) -> mapLeft.putAll(mapRight));
return output;
}
回答3:
One way to do it could be -
Map<V1,Set<V2>> inputHashMap = new HashMap<>(); // initialized with your input
Map<V2,Set<V1>> outputHashMap = new HashMap<>();
inputHashMap.forEach((val, keys) -> keys.forEach(key -> {
if (outputHashMap.containsKey(key)) {
outputHashMap.get(key).add(val);
} else {
outputHashMap.put(key, new HashSet<>() {{
add(val);
}});
}
}));
回答4:
Using stream (might be good for parallel processing using parallel()
on stream)
Map<String, Set<String>> inMap = new HashMap<>();
inMap.put("key1", new HashSet<>(Arrays.asList("a", "b", "c")));
inMap.put("key2", new HashSet<>(Arrays.asList("c", "d")));
Map<String, Set<String>> outMap = inMap.entrySet().stream().collect(
HashMap::new,
(m, e) -> e.getValue().forEach(v -> m.computeIfAbsent(v, ignore -> new HashSet<>())
.add(e.getKey())),
(m1, m2) -> m2.forEach((key, value) -> m1.merge(key, value,
(s1, s2) -> { s1.addAll(s2); return s1; })));
System.out.println(outMap);
// {a=[key1], b=[key1], c=[key1, key2], d=[key2]}
Of course old school for
loop is much more cleaner
Map<String, Set<String>> outMap = new HashMap<>();
for (Entry<String, Set<String>> e : inMap.entrySet())
for (String v : e.getValue())
outMap.computeIfAbsent(v, key -> new HashSet<>()).add(e.getKey());
Less LOC
Map<String, Set<String>> outMap = new HashMap<>();
inMap.forEach((k, v) -> v.forEach(e ->
outMap.computeIfAbsent(e, __ -> new HashSet<>()).add(k)));
来源:https://stackoverflow.com/questions/46480725/how-do-you-invert-mapv1-setv2-to-mapv2-setv1-in-java-with-stream