如何在MultiMap中计算每个值的出现次数?(java)

3

我有一个包含两个字符串的 Multimap。例如:

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

多重映射定义(我使用的是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()) {
        }
    }      

我想要计算multimap中相同值的出现次数。
因此,如果我在multimap中计算值[key, car]的数量(例如上面给出的示例),则结果应该是2:
  • [key,car]的出现次数= 2
  • [key,blue]的出现次数= 1
我也尝试使用多值HashMap实现这一点,并通过以下方式进行计数(Storage是存储两个字符串值的对象类):
B = Collections.frequency(new ArrayList<Storage>(map.values()), map.get(number));

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

你的Multipmap定义是什么样的? - undefined
你想要统计多重映射中不同元组的数量吗?为什么不将它们全部添加到一个Set中,然后获取其大小呢? - undefined
@AntonAdanasjew 我想要计算相同出现的次数,而不是MultiMap中有多少个不同的元组。 :) - undefined
由于您的问题有点模糊,如果您能提供您示例的期望输出,将会很有帮助。 - undefined
@booyah 然后你可以从结果集中生成一个流,并使用groupingBy收集器。 - undefined
显示剩余2条评论
4个回答

2

您可以通过创建一个将您的multimap值作为键,计数作为值的映射来实现您想要的效果:

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 方法来累加相等的值,通过计算其出现次数。

1
请尝试这段代码。 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 是的,我需要比较两个字符串(存储在同一个键下的multimap条目的值),与同一个键下的另一个值进行比较。所以这个答案并不完全是我正在寻找的解决方案,在这种情况下,我们只需要一个字符串并进行比较。 - undefined
你在问题中说:“我想要统计Multimap中相同值的出现次数”。当你使用“map_multi.get(key)”时,你会得到与该键相关联的所有值。之后,你可以将所有的值进行比较。 - undefined
@ramazankul 嗯,我会查看这个的。 - undefined
@ramazankul 我觉得这只是计算一个键下数值的数量,不完全符合我的要求。 - undefined

1

首先,您需要从ListMultimap创建一个Map<Integer, List<String>>。您可以按照以下方式完成:

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

接下来假设你有一个包含carkeyList<String>

 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 - undefined
2
整个第一次转换是ListMultimap的内置功能:使用Multimap#asMap()(获取Map<Integer,Collection<String>>视图)或Multimaps#asMap(ListMultimap)(获取类型强制转换的Map<Integer,List<String>>视图)。 - undefined

1
我认为你使用了错误的集合来存储你的数据。根据你所写的内容,你想要一个由整数键和两个元素元组作为值的映射,然后使用Multiset来计算频率:

Multiset是一个支持无序相等的集合,类似于Set,但可以有重复元素。Multiset有时也被称为bag

相互之间相等的Multiset元素被称为同一单个元素的出现次数。一个元素在Multiset中的总出现次数称为该元素的计数(术语“频率”和“多重性”是等效的,但不在此API中使用)

下面的代码假设您已经正确实现了两个元素元组(也称为对,或者您的Storage类,但具有适当的equalshashCode实现),例如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),并且放置不会覆盖第一个值(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中 :( - undefined
你其实不一定要使用jOOL,你可以自己编写这个类(类似于你的Storage)- 同样,它只需要正确实现hashCodeequals方法。我已经编辑了答案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接