【问题标题】:How to count occurrences for each value in MultiMap ? (java)如何计算 MultiMap 中每个值的出现次数? (爪哇)
【发布时间】:2018-01-18 19:55:37
【问题描述】:

我有Multimap,它包含两个字符串。示例:

1 = [key,car],  
2 = [key,blue],
3 = [key,car]

Multimap 定义(我正在使用 Guava 库):

ListMultimap<Integer, String> map_multi = ArrayListMultimap.create(); 

这就是我在 MultiMap 中输入值的方式:

for (int i = 0; i < list.size(); i++) {
        if (i + 1 < list.size()) {

            multimap.put(i,(String) list.get(i));
            multimap.put(i,(String) list.get(i+1));

        } else if (i + 1 == list.size()) {
        }
    }      

我想计算多图中相同值的出现次数。

所以如果我计算我的多重映射中有多少个值[key,car](我上面给出的示例),结果应该是 2:

  • 出现[key,car] = 2
  • 出现[key,blue] = 1

我也尝试用多值HashMap 来实现它,我用这个来计算它,方式(Storage 是我在对象内存储两个字符串值的类):

B = Collections.frequency(new ArrayList<Storage>(map.values()), map.get(number));

但我没有得到正确的结果。

【问题讨论】:

  • 您的 Multipmap 定义是什么样的?
  • @ramazankul 我已经编辑了我的帖子
  • 您想计算多图中所有不同的元组吗?为什么不将它们全部添加到 Set 中并获取其大小?
  • @AntonAdanasjew 我想计算相同的出现次数,而不是 MultiMap 中有多少不同的元组。 :)
  • 由于您的问题有点模糊,如果您也为您的示例显示所需的输出将会很有帮助。

标签: java hashmap guava multimap


【解决方案1】:

您可以通过创建一个将您的多图值作为键、计数作为值的映射来实现您想要的:

Map<Collection<String>, Long> result = map_multi.asMap().values().stream()
    .collect(Collectors.groupingBy(v -> v, Collectors.counting()));

这里我使用 Guava 的 Multimap.asMap 方法在原始多地图上获取 视图,然后将值收集到新地图中。

另一种方式,没有流:

Map<Collection<String>, Integer> result = new HashMap<>();
map_multi.asMap().values().forEach(v -> result.merge(v, 1, Integer::sum));

这使用Map.merge 方法通过计算其出现次数来累积相等的值。

【讨论】:

    【解决方案2】:

    请试试这个代码。 map_multi.get(key).size() 是你的答案。

    ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
    map_multi.put(1, "car");
    map_multi.put(2, "blue");
    map_multi.put(3, "apple");
    map_multi.put(1, "car");
    
    for (Integer key : map_multi.keySet()) {
        System.out.println(map_multi.get(key).get(0) + " occurances: " + map_multi.get(key).size());
    }
    

    输出:

    car occurances: 2
    blue occurances: 1
    apple occurances: 1
    

    【讨论】:

    • @Schidu Luca 是的,我需要比较存储在同一个键下的两个字符串(多映射条目的值),另一个 valuea 在同一个键下,所以这个答案不是我正在寻找的解决方案因为,在这种情况下,我们只寻找一个字符串并进行比较
    • 您在问题中说:“我想计算 Multimap 中相同值的出现次数”。当您执行“map_multi.get(key)”时,您将获得连接此键的所有值。在此之后,您可以比较所有值女巫。
    • @ramazankul 嗯,我会检查一下
    • @ramazankul 我认为这只是计算一个键下的值的数量,而不是我想要的
    【解决方案3】:

    所以第一步是从您的ListMultimap 创建一个Map&lt;Integer, List&lt;String&gt;&gt;。您可以通过以下方式执行此操作:

    Map<Integer, List<String>> collect = map_multi.entries()
                .stream()
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                         Collectors.mapping(Map.Entry::getValue,
                                            Collectors.toList())));
    

    假设你有一个List&lt;String&gt;,其中包含carkey。 示例:

     List<String> myList = List.of("key", "car"); // java 9
    

    您只需遍历地图的values() 并检查myList 是否包含地图列表中的所有元素。

    long count = collect.values()
                .stream()
                .filter(list -> list.containsAll(myList))
                .count();
    

    【讨论】:

    • 我认为这是解决方案。但正如我正在阅读的那样,首先实现 Map 而不是转换它并不容易。这应该给我同样的结果吧? @Schidu Luca
    • 第一个转换是ListMutlimap 的内置功能:使用Multimap#asMap()(获取Map&lt;Integer, Collection&lt;String&gt;&gt; 视图)或Multimaps#asMap(ListMultimap)(获取强制类型Map&lt;Integer, List&lt;String&gt;&gt; 视图)。
    【解决方案4】:

    我认为您使用错误的集合来存储您的数据。根据您编写的内容,您需要一个以整数键和二元元组作为值的映射,然后使用 Multiset 来计算频率:

    Multiset一个支持顺序无关相等的集合,如Set,但可能有重复的元素。多重集有时也称为 bag

    多重集合中彼此相等的元素被称为相同单个元素的出现。 一个元素在多重集中出现的总次数称为该元素的计数(术语“频率”和“多重性”是等价的,但未在此 API 中使用)。

    下面的代码假设您正确实现了二元素元组(又名对,或您的 Storage 类,但具有正确的 equalshashCode 实现),例如 this one from jOOL

    HashMap<Integer, Tuple2<String, String>> m = new HashMap<>();
    Tuple2<String, String> carTuple = new Tuple2<>("key", "car");
    Tuple2<String, String> blueTuple = new Tuple2<>("key", "blue");
    
    m.put(1, carTuple);
    m.put(2, blueTuple);
    m.put(3, carTuple);
    
    ImmutableMultiset<Tuple2<String, String>> occurrences = 
        ImmutableMultiset.copyOf(m.values());
    System.out.println(occurrences); // [(key, car) x 2, (key, blue)]
    

    如果您需要在一个键(整数)下映射几个值(元组),那么您应该将第一行更改为 multimap:

    ListMultimap<Integer, Tuple2<String, String>> m = ArrayListMultimap.create();
    

    这样m.put(1, anotherTuple) 是可能的,并且 put 不会覆盖第一个值 (carTuple),而是将其添加到 1 值列表下。

    编辑:

    如果你不需要/想要额外的依赖,你可以自己实现Tuple2,它可能看起来像这个类:

    public class Tuple2<T1, T2> {
    
      public final T1 v1;
      public final T2 v2;
    
      public Tuple2(T1 v1, T2 v2) {
        this.v1 = v1;
        this.v2 = v2;
      }
    
      @Override
      public boolean equals(Object o) {
        if (this == o) {
          return true;
        }
        if (!(o instanceof Tuple2)) {
          return false;
        }
        @SuppressWarnings({"unchecked", "rawtypes"}) final Tuple2<T1, T2> that = (Tuple2) o;
        return Objects.equals(v1, that.v1) && Objects.equals(v2, that.v2);
      }
    
      @Override
      public int hashCode() {
        return Objects.hash(v1, v2);
      }
    
      @Override
      public String toString() {
        return "(" + v1 + ", " + v2 + ")";
      }
    }
    

    【讨论】:

    • 我认为这应该可以解决问题,但我有一个新手问题。我不知道如何将 jooq 导入 Netbeans :(
    • 您实际上不必使用 jOOL,您可以自己编写类(类似于您的 Storage) - 再次,它所需要的只是正确的 hashCodeequals 实现。我已经编辑了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-12
    相关资源
    最近更新 更多