Java Collections Frequency: Counting Occurrences
java collections frequency: Learn how to use Collections.frequency() to count element occurrences in Java collections, understand equality rules, performance, and alte...
What Collections.frequency Returns
When a Java developer searches for java collections frequency, the subject is almost always the Collections.frequency() method. It counts how many elements in a collection are equal to a given object, returns an int, and performs a linear scan of the collection, comparing each element to the target using equals().
The method is part of java.util.Collections, so no additional dependency is needed. A typical call looks like this:
List<String> words = Arrays.asList("java", "collections", "java", "frequency"); int count = Collections.frequency(words, "java"); System.out.println(count); // 2
The target object is passed as Object, not as a generic type parameter. That means the method compiles even when the target's type does not match the collection's element type. A mismatched type simply yields zero, because no element will be equal to it.
Basic Usage with a List
Lists are the most common use case because they allow duplicate elements. The method counts every occurrence that satisfies the equality check.
List<Integer> scores = Arrays.asList(10, 20, 10, 30, 10, 40); int tens = Collections.frequency(scores, 10); System.out.println(tens); // 3
The same pattern works for any Collection implementation, including ArrayList, LinkedList, and Vector. The runtime cost is proportional to the number of elements, since the collection is traversed once.
For a Set, the result is always either 0 or 1. A set cannot contain duplicates, so frequency can only confirm presence or absence. Using it on a set is rarely useful; contains is the more direct API for that case.
How Equality Is Determined
The method relies entirely on the equals() contract. For standard types such as String, Integer, and LocalDate, equality is well defined, and the method behaves as expected.
For custom objects, the behavior depends on whether equals() is overridden. Consider a Person class that does not override equals():
class Person { String name; Person(String name) { this.name = name; } } List<Person> people = Arrays.asList(new Person("alice"), new Person("alice")); int count = Collections.frequency(people, new Person("alice")); System.out.println(count); // 0
Without an equals() override, Object.equals() performs reference comparison. The two Person instances are different objects, so the count is zero even though the names match. Overriding equals() (and hashCode(), for consistency) makes the method compare by value instead.
When overriding equals(), remember that Collections.frequency calls equals() on each element with the target as the argument. The element's equals() implementation must therefore handle a target of a different type gracefully by returning false.
Edge Cases: Nulls, Sets, and Maps
Passing null as the target works: the method counts elements that are null. This is useful when a collection may contain null values and you need to know how many there are.
List<String> items = Arrays.asList("a", null, "b", null); System.out.println(Collections.frequency(items, null)); // 2
A null collection argument throws NullPointerException, since the method immediately tries to iterate the collection. Guard against that if the collection may be null.
For a Map, Collections.frequency does not operate on the map directly. You must pass either map.keySet() or map.values() depending on what you want to count.
Map<String, Integer> map = new HashMap<>(); map.put("a", 1); map.put("b", 1); map.put("c", 2); int ones = Collections.frequency(map.values(), 1); System.out.println(ones); // 2
Counting values this way is straightforward but only useful for small maps. For larger maps, building a frequency map is more efficient if you need counts for many distinct values.
Performance and Repeated Counting
Collections.frequency performs a single linear pass, so its time complexity is O(n), where n is the collection size. For a one-off count, that is the simplest and most readable option.
The cost becomes a problem when you call frequency repeatedly on the same collection with different targets. Each call rescans the entire collection, producing O(n × m) total work for m distinct targets.
List<String> words = loadWords(); for (String target : targets) { int count = Collections.frequency(words, target); // rescans every time }
If you need counts for many targets, build a frequency map once:
Map<String, Integer> counts = new HashMap<>(); for (String word : words) { counts.merge(word, 1, Integer::sum); }
Subsequent lookups are O(1) per target instead of O(n). The tradeoff is the upfront cost of building the map and the extra memory it consumes. For a single target, Collections.frequency is usually the better choice; for many targets, the map wins.
Alternatives to Collections.frequency
Java streams provide a counting alternative:
long count = words.stream() .filter(word -> word.equals("java")) .count();
The stream version returns a long and reads fluently when combined with other stream operations. It is slightly more verbose for a simple count, and it adds stream setup overhead that is irrelevant for small collections.
Another option is Collections.frequency on a List obtained from Arrays.asList, which is what most examples show. For arrays specifically, there is no direct Collections.frequency overload; you must convert the array to a list first or iterate manually.
String[] array = {"a", "b", "a"}; int count = Collections.frequency(Arrays.asList(array), "a");
That conversion copies the array into a fixed-size list, which is a minor allocation. For large arrays, a manual loop avoids that allocation:
int count = 0; for (String s : array) { if (s.equals("a")) count++; }
Choosing the Right Counting Approach
The decision depends on how many counts you need and whether the collection changes between queries.
Use Collections.frequency when you need a single count for one target, the collection is small or medium sized, and readability matters more than micro-optimization.
Use a frequency map when you need counts for many distinct targets, the same collection is queried repeatedly, and the collection is large enough that repeated linear scans would dominate runtime.
Use streams when you are already chaining stream operations such as filter, map, or groupingBy, or when you prefer the functional style and the count is part of a larger pipeline.
The Collections.frequency method is the right default for simple counting because it is concise, dependency-free, and easy to read. The alternatives earn their complexity only when the usage pattern justifies them.