簡體   English   中英

用冗余值反轉 Map 以生成多映射

[英]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 CollectionsGoogle 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)並生成多圖,其中值可以是列表集合

  1. Multimap 值是一個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; }));
  2. Multimap 值是一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM