I have been using the Counter() data structure in Python as a key-value store that allows me to have the objects sorted according to their value using the most_common method. More info here.
Is there any similar data structure for the Java language? For example, I have seen many related answers that focus on sorting HashMaps or TreeMaps by the data structure is not initially defined for that purpose. In my case I usually need to to keep counters of objects and then to select the most common or the ones with the highest score (Top-N queries). However, it is difficult for me since I need to insert to a HashMap and then sort or to use multiple data structures.
Counter is a subclass of dict that's specially designed for counting hashable objects in Python. It's a dictionary that stores objects as keys and counts as values. To count with Counter , you typically provide a sequence or iterable of hashable objects as an argument to the class's constructor.
Using the Python Counter tool, you can count the key-value pairs in an object, also called a hashtable object. The Counter holds the data in an unordered collection, just like hashtable objects. The elements here represent the keys and the count as values. It allows you to count the items in an iterable list.
try-with-resources is its Java equivalent, and is available in Java 7 and up. This is the try-with-resources construct.
From here:
The Counter class is similar to bags or multisets in other languages.
Java does not have a Multiset class, or an analogue. Guava has a MultiSet collection, that does exactly what you want.
In pure Java, you can use a Map and the new merge method:
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));
Output:
2
3
0
You can wrap this behaviour in a class in a few lines of code:
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);
}
}
And use it like this:
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"));
Output:
2
3
0
Here's a class that looks like it implements enough of Counter
to do what you want.
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));
}
It uses Java 8 functionality.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With