java—使用冗余值反转Map以生成多重Map

bt1cpqcv  于 2021-06-29  发布在  Java
关注(0)|答案(4)|浏览(367)

给出这样一张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}
…如何反转以生成一个不同编号的多重Map,每个编号指向一个集合(列表?套?) DayOfWeek 谁拥有那个号码?
结果应与此代码的结果相等:

Map.of(
    53 , List.of( DayOfWeek.WEDNESDAY , DayOfWeek.THURSDAY ) ,
    52 , List.of( DayOfWeek.MONDAY , DayOfWeek.TUESDAY , DayOfWeek.FRIDAY , DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) 
)

我想使用纯java生成结果multimap,而不需要额外的库,比如eclipse集合或google guava。这些库可能会使这更容易,但我很好奇,看看是否一个解决方案只使用内置java是可能的。否则,我这里的问题和guava完全一样:通过反转一个Map来构造一个多重Map。考虑到现代java中新的流和多Map特性,我希望这现在是可能的,而那时不是。
我看到了很多类似的问题。但没有一个符合我的情况,这似乎是一个相当普遍的情况。例如,这个问题忽略了原始值是冗余/多重的问题,因此需要一个多重Map。其他像这样或这样涉及谷歌Guava。

shstlldc

shstlldc1#

请参考以下代码:

@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);
}
oknrviil

oknrviil2#

以下是使用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集合:

@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集合的提交者。

cbeh67ev

cbeh67ev3#

收集器.tomap

在这种情况下,您可以使用 Collectors.toMap​(keyMapper,valueMapper,mergeFunction) 并生成多重Map,其中值可以是列表或集合:
多重Map值是 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, i.e. lists
                (list1, list2) -> {
                    List<DayOfWeek> list = new ArrayList<>();
                    list.addAll(list1);
                    list.addAll(list2);
                    return list;
                }));

多重Map值是 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, i.e. sets
                (set1, set2) -> {
                    Set<DayOfWeek> set = new HashSet<>();
                    set.addAll(set1);
                    set.addAll(set2);
                    return set;
                }));

另请参见:基于多个字段收集ID列表

zujrkrfu

zujrkrfu4#

使用streams,可以将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)))
  ));

相关问题