Java 的 Python Counter 替代方案

21

我一直在使用Python中的Counter()数据结构作为键值存储,它允许我使用most_common方法按其值对对象进行排序。更多信息请参见这里

Java语言中是否有类似的数据结构?例如,我看到许多相关答案都集中在对HashMapsTreeMaps进行排序,但这些数据结构最初并不是为此目的而定义的。在我的情况下,我通常需要计数器来统计对象,然后选择最常见或得分最高的对象(Top-N查询)。然而,这对我来说很困难,因为我需要先将对象插入HashMap,然后再进行排序或使用多个数据结构。


也许你正在寻找的是这个问题的答案。 - Ferdinand Neman
这个回答解决了你的问题吗?Scala/Java中是否有类似于Python 3中collections.Counter的功能? - eshizhan
2个回答

17

来自这里

Counter类类似于其他语言中的bags或multisets。

Java没有Multiset类或相似的类。Guava有一个MultiSet集合,可以完全满足你的需求。

在纯Java中,您可以使用Map和新的merge方法:

final Map<String, Integer> counts = new HashMap<>();

counts.merge("Test", 1, Integer::sum);
counts.merge("Test", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);

System.out.println(counts.getOrDefault("Test", 0));
System.out.println(counts.getOrDefault("Other", 0));
System.out.println(counts.getOrDefault("Another", 0));

输出:

2
3
0

你可以在几行代码中将此行为封装在一个类中:
public class Counter<T> {
    final Map<T, Integer> counts = new HashMap<>();

    public void add(T t) {
        counts.merge(t, 1, Integer::sum);
    }

    public int count(T t) {
        return counts.getOrDefault(t, 0);
    }
}

并像这样使用它:

final Counter<String> counts = new Counter<>();

counts.add("Test");
counts.add("Test");
counts.add("Other");
counts.add("Other");
counts.add("Other");

System.out.println(counts.count("Test"));
System.out.println(counts.count("Other"));
System.out.println(counts.count("Another"));

输出:

2
3
0

6
这是一个看起来实现了足够多的Counter类,可以满足你的需求。
static class Counter<T> {

    final ConcurrentMap<T, Integer> counts = new ConcurrentHashMap<>();

    public void put(T it) {
        add(it, 1);
    }

    public void add(T it, int v) {
        counts.merge(it, v, Integer::sum);
    }

    public List<T> mostCommon(int n) {
        return counts.entrySet().stream()
                // Sort by value.
                .sorted((e1, e2) -> Integer.compare(e2.getValue(), e1.getValue()))
                // Top n.
                .limit(n)
                // Keys only.
                .map(e -> e.getKey())
                // As a list.
                .collect(Collectors.toList());
    }
}

public void test() {
    Counter<String> c = new Counter<>();
    String[] numbers = {"Zero", "One", "Two", "Three", "Four", "Five", "Six"};
    for (int i = 0; i < numbers.length; i++) {
        c.add(numbers[i], i);
    }
    System.out.println(c.mostCommon(3));
}

它使用Java 8的功能。


3
我登录只是为了点赞 - 谢谢。我还注意到mostCommon方法实际上返回的是最不常见的元素。这个修改被拒绝了,我也不太确定为什么。但我至少想给那些使用它的人留个备注 - 在比较时将Integer.compare(e1.getValue(), e2.getValue())改为Integer.compare(e2.getValue(), e1.getValue())以获取最常见的元素。 - Myles Hollowed
@MylesHollowed - 很好的发现 - 感谢提醒 - 已修复。 - OldCurmudgeon

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