[英]Invert a Map with redundant values to produce a multimap
给定这样的 map,我们有一年中每周的频率计数:
Map.of(
DayOfWeek.MONDAY , 52 ,
DayOfWeek.TUESDAY , 52 ,
DayOfWeek.WEDNESDAY, 53 ,
DayOfWeek.THURSDAY , 53 ,
DayOfWeek.FRIDAY , 52 ,
DayOfWeek.SATURDAY , 52 ,
DayOfWeek.SUNDAY , 52
)
…或作为文本:
{周一=52,周二=52,周三=53,周四=53,周五=52,周六=52,周日=52}
...我如何反转以生成不同数字的多重映射,每个数字都导致拥有该数字的DayOfWeek
的集合(列表?集合?)?
结果应该等同于这段代码的结果:
Map.of(
53 , List.of( DayOfWeek.WEDNESDAY , DayOfWeek.THURSDAY ) ,
52 , List.of( DayOfWeek.MONDAY , DayOfWeek.TUESDAY , DayOfWeek.FRIDAY , DayOfWeek.SATURDAY , DayOfWeek.SUNDAY )
)
我想使用直接的Java生成生成的多图,而不需要额外的库,例如Eclipse Collections或Google Guava 。 这些库可能会使这更容易,但我很好奇是否可以使用仅使用内置 Java 的解决方案。 否则,我的问题与Guava 完全相同:通过反转 Map 来构造多图。 鉴于现代 Java 中的新流和多地图功能,我希望现在这是可能的,而当时还没有。
我看到了与此类似的各种现有问题。 但没有一个适合我的情况,这似乎是一种相当普遍的情况。 例如,这个问题忽略了原始值是冗余/多重的问题,因此需要一个多重映射。 诸如此类或此类的其他涉及Google Guava。
以下作品使用 Java 9 或更高版本:
@Test
void invertMap()
{
Map<DayOfWeek, Integer> map = Map.of(
DayOfWeek.MONDAY, 52,
DayOfWeek.TUESDAY, 52,
DayOfWeek.WEDNESDAY, 53,
DayOfWeek.THURSDAY, 53,
DayOfWeek.FRIDAY, 52,
DayOfWeek.SATURDAY, 52,
DayOfWeek.SUNDAY, 52
);
Map<Integer, Set<DayOfWeek>> flipped = new TreeMap<>();
map.forEach((dow, count) ->
flipped.computeIfAbsent(count, (key) ->
EnumSet.noneOf(DayOfWeek.class)).add(dow));
Map<Integer, Set<DayOfWeek>> flippedStream = map.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
TreeMap::new,
Collectors.mapping(
Map.Entry::getKey,
Collectors.toCollection(
() -> EnumSet.noneOf(DayOfWeek.class)))));
Map<Integer, Set<DayOfWeek>> expected = Map.of(
53, EnumSet.of(
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY),
52, EnumSet.of(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.FRIDAY,
DayOfWeek.SATURDAY,
DayOfWeek.SUNDAY)
);
Assert.assertEquals(expected, flipped);
Assert.assertEquals(expected, flippedStream);
}
如果您愿意使用第三方库,以下代码将适用于Eclipse Collections :
@Test
void invertEclipseCollectionsMap()
{
MutableMap<DayOfWeek, Integer> map =
Maps.mutable.<DayOfWeek, Integer>empty()
.withKeyValue(DayOfWeek.MONDAY, 52)
.withKeyValue(DayOfWeek.TUESDAY, 52)
.withKeyValue(DayOfWeek.WEDNESDAY, 53)
.withKeyValue(DayOfWeek.THURSDAY, 53)
.withKeyValue(DayOfWeek.FRIDAY, 52)
.withKeyValue(DayOfWeek.SATURDAY, 52)
.withKeyValue(DayOfWeek.SUNDAY, 52);
SetMultimap<Integer, DayOfWeek> flipped = map.flip();
Assert.assertEquals(flipped.get(52), Set.of(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.FRIDAY,
DayOfWeek.SATURDAY,
DayOfWeek.SUNDAY));
Assert.assertEquals(flipped.get(53), Set.of(
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY));
}
注意:我是 Eclipse Collections 的提交者。
使用流,您可以将 map 拆分为其条目,然后翻转条目和组:
numberOfDaysInYear.entrySet().stream()
.collect(groupingBy(Map.Entry::getValue), mapping(Map.Entry::getKey, toList()));
根据您更新的评论,要求优化实际上不在您的原始问题中,
numberOfDaysInYear.entrySet().stream()
.collect(groupingBy(
Map.Entry::getValue,
TreeMap::new,
mapping(Map.Entry::getKey, toCollection(() -> EnumSet.of(DayOfWeek.class)))
));
Collectors.toMap
在这种情况下,您可以使用方法Collectors.toMap(keyMapper,valueMapper,mergeFunction)
并生成多图,其中值可以是列表或集合:
List
: Map<Integer, List<DayOfWeek>> inverted = map.entrySet().stream().collect(Collectors.toMap( // key of the new map entry -> entry.getValue(), // value of the new map entry -> List.of(entry.getKey()), // merging two values, ie lists (list1, list2) -> { List<DayOfWeek> list = new ArrayList<>(); list.addAll(list1); list.addAll(list2); return list; }));
Set
: Map<Integer, Set<DayOfWeek>> inverted = map.entrySet().stream().collect(Collectors.toMap( // key of the new map entry -> entry.getValue(), // value of the new map entry -> Set.of(entry.getKey()), // merging two values, ie sets (set1, set2) -> { Set<DayOfWeek> set = new HashSet<>(); set.addAll(set1); set.addAll(set2); return set; }));
另请参阅:收集基于多个字段的 id 列表
请参考以下代码:
@Test
void testMap() {
Map<DayOfWeek, Integer> map = new HashMap<>();
map.put(DayOfWeek.MONDAY, 52);
map.put(DayOfWeek.TUESDAY, 52);
map.put(DayOfWeek.WEDNESDAY, 53);
map.put(DayOfWeek.THURSDAY, 53);
map.put(DayOfWeek.FRIDAY, 52);
map.put(DayOfWeek.SATURDAY, 52);
map.put(DayOfWeek.SUNDAY, 52);
Map<Integer, List<DayOfWeek>> result = new HashMap<>();
for (Map.Entry<DayOfWeek, Integer> entry : map.entrySet()) {
if (result.containsKey(entry.getValue())) {
List list = result.get(entry.getValue());
list.add(entry.getKey());
result.put(entry.getValue(), list);
} else {
List list = new ArrayList();
list.add(entry.getKey());
result.put(entry.getValue(), list);
}
}
System.out.println(result);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.