在 Multimap 中如何 return 出现次数最多的值

In Multimap how to return value which has highest occurrence

我有来自 guava 库的 MultiMap

Multimap<Integer,String> maps = ArrayListMultimap.create();

      maps.put(1, "foo");
      maps.put(1, "bar");
      maps.put(1, "foo");
      maps.put(2, "Hello");
      maps.put(2, "foo");
      maps.put(2, "World");
      maps.put(2, "World");

在此对于键 1,我需要 return 出现次数最多的值。在上面的例子中,它必须 return 映射为

预期结果:

[1,foo]
[2,World]

我试过了

Stream result1 = maps.keySet().stream() 
                  .map(i -> 
                              maps.get(i).stream() 
                                  .collect(
                                          Collectors.groupingBy(v -> v, Collectors.counting())
                                          )
                                  );

结果:

{{bar=1, foo=2}=1, {Hello=1, foo=1, World=2}=1}

您似乎正在寻找的东西可以在迭代条目时实现:

Map<Integer, String> integerStringMap = maps.asMap()
        .entrySet()
        .stream()
        .collect(Collectors.toMap(Map.Entry::getKey,
                e -> mostFrequentWord(e.getValue())));

mostFrequentWord的实现应该return一个String如:

static String mostFrequentWord(Collection<String> values) {
    return values.stream()
            .collect(Collectors.groupingBy(v -> v, Collectors.counting()))
            .entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElseThrow(() -> new UnsupportedOperationException(
                    "Empty collection as values in multimap"));
}
maps.keySet().stream().distinct().collect(Collectors.toMap(key -> key, key -> {
    Map<String, Long> valueByCount = maps.get(key).stream().collect(Collectors.groupingBy(s -> s, Collectors.counting()));
    return valueByCount.entrySet()
        .stream()
        .max(Comparator.comparing(Map.Entry::getValue))
        .map(Map.Entry::getKey)
        .orElse(null);
}));

你可以这样做:

var valueFrequency = maps.entries().stream()
          .collect(groupingBy(Function.identity(), counting()));

var result = valueFrequency.entrySet()
                  .stream()
                  .max(Map.Entry.comparingByValue())
                  .stream()
                  .flatMap(maxFreq -> valueFrequency.entrySet().stream()
                                          .filter(val -> val.getValue().equals(maxFreq.getValue()))
                                          .map(Map.Entry::getKey))
                  .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

这不会非常直截了当。显然,您首先需要按键分组。然后基于 Key,您需要找到相应 Value 的最大出现次数(例如 1 == foo)。找到max的唯一方法是遍历映射到某个键的Collection<String>。这使事情变得更加复杂,因为您使用 Multimap 并且您可以轻松地拥有以下内容:

    maps.put(1, "foo");
    maps.put(1, "bar");
    maps.put(1, "bar");
    maps.put(1, "foo");

因此,IMO,这可以写成:

Map<Integer, List<String>> result =
        maps.keySet()
            .stream()
            .collect(Collectors.toMap(
                Function.identity(),
                x -> {
                    Map<String, Long> freqMap = maps.get(x)
                                                    .stream()
                                                    .collect(Collectors.groupingBy(
                                                        Function.identity(),
                                                        Collectors.counting())
                                                    );
                    long max = Collections.max(freqMap.values());
                    return freqMap.entrySet()
                                  .stream()
                                  .filter(y -> y.getValue() == max)
                                  .map(Entry::getKey)
                                  .collect(Collectors.toList());
                }
            ));
  • 我先按 Key12)分组
  • 然后获取映射到该 Key
  • Collection<String>
  • 然后计算表示值频率的 Map<String, Long>。例如:["foo" = 2]; ["bar" = 1]
  • 然后我查看最大出现次数。由于您使用的是多图,对于相同的 Key,您可能会遇到 ["foo" = 2]; ["bar" = 2] 的情况,因此我们需要将 foobar 作为结果。
  • 基于那个max,我找出了相应的值。

没有流,但使用 Guava 之类的东西 Multiset and Maps.transformValues:

@Test
public void shouldFindHightestOccurrencesInMultimapValues() {
    //given
    Multimap<Integer, String> maps = ArrayListMultimap.create();
    maps.put(1, "foo");
    maps.put(1, "bar");
    maps.put(1, "foo");
    maps.put(2, "Hello");
    maps.put(2, "foo");
    maps.put(2, "World");
    maps.put(2, "World");
    //when
    Map<Integer, String> result = ImmutableMap.copyOf(Maps.transformValues(maps.asMap(), this::findHighestOccurrence));
    //then
    assertThat(result).containsOnly(
            entry(1, "foo"),
            entry(2, "World"));
}

private String findHighestOccurrence(Collection<String> values) {
    return Multisets.copyHighestCountFirst(ImmutableMultiset.copyOf(values)).iterator().next();
}

如果有一个 MultisetMultimap 子类型(即 Map<K, Multiset<V>>-like specialized Multimap,在这种情况下它可能是存储数据的最佳结构。