Collectors.counting()

Counts the elements of a stream or of each group, and explains the Long result and when Stream.count() is the better call.

Signature
public static <T> Collector<T, ?, Long> counting()
  • Added in Java Java 8

Return value

Collector<T, ?, Long>A collector that produces the number of input elements as a Long, or 0 when there are none.

Errors

Does not throw.

Collectors.counting() counts the elements it receives and returns the number as a Long. On its own it does the same as Stream.count(). Its real use is as a downstream collector, where it turns groupingBy into a frequency table:

Map<String, Long> perLevel = lines.stream()
    .collect(Collectors.groupingBy(LogLine::level, Collectors.counting()));

That is the in-memory equivalent of SELECT level, COUNT(*) ... GROUP BY level, and the same pattern counts requests per endpoint, signups per country or errors per customer.

Why Long and not Integer

A stream is not limited to Integer.MAX_VALUE elements, so the count is a long, boxed as Long because a collector result is an object. This trips people up when a DTO or an existing method expects Map<String, Integer>: the code does not compile. Either change the type, or convert inside the collector with collectingAndThen(counting(), Long::intValue), or use summingInt(x -> 1), which gives an Integer directly.

counting() or count()?

For a single total, call stream.count(). It reads better, returns a primitive long, and since Java 9 it may skip running the pipeline at all when the size is known from the source, for example a list with only map steps. That also means side effects in peek or map may not run, so never rely on them for work that must happen. Keep counting() for places that need a Collector: inside groupingBy, partitioningBy or a teeing collector.

Counting what you meant

counting() counts elements, not distinct values. Counting log events per day is not the same as counting unique users per day; for the second, collect the user ids into a set per group and take its size. And remember that groups only exist for keys that appeared, so a key with zero occurrences is missing from the map, not mapped to 0.

When the data lives in a database, a COUNT(*) query is cheaper than loading rows to count them. Use counting() for data already in memory: parsed log files, API responses, a page of results.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Building a per-level count of log lines for an error-rate alert.
  • Counting API calls per client key from an access log to spot one client hammering an endpoint.
  • Returning order counts per status in a JSON summary for an admin dashboard.

Frequently asked questions

Why does Collectors.counting return Long instead of Integer?

Streams can hold more than Integer.MAX_VALUE elements, so the count is a long, boxed as Long. For a Map<K, Integer>, use collectingAndThen(Collectors.counting(), Long::intValue) or summingInt(x -> 1).

Should I use Collectors.counting() or Stream.count()?

Use stream.count() for a single total; it returns a primitive long and may avoid running the pipeline when the size is already known. Use Collectors.counting() where a Collector is required, mainly as the downstream of groupingBy or partitioningBy.

How do I count occurrences of each element in a list in Java?

list.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) returns a Map from each distinct element to how many times it occurs. Add TreeMap::new as the middle argument for sorted keys.

More examples

Integer counts for a DTO

collectingAndThen applies Long::intValue to each group's count, so the map matches the Integer type the record declares. Long::intValue does not check for overflow, which is safe for any count of elements you could hold in a list.

Example · java
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

record StatusReport(Map<String, Integer> countsByStatus) {}

void main() {
    List<String> statuses = List.of("PAID", "NEW", "PAID", "SHIPPED", "PAID");

    Map<String, Integer> counts = statuses.stream()
        .collect(Collectors.groupingBy(s -> s, TreeMap::new,
            Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
    IO.println(new StatusReport(counts));
}
// Output:
// StatusReport[countsByStatus={NEW=1, PAID=3, SHIPPED=1}]

The most requested path

Count first, then stream the entries and pick the largest value. The intermediate map is a HashMap, which is fine here because only the maximum is printed; with ties, which key wins would depend on that map's order.

Example · java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

void main() {
    List<String> paths = List.of(
        "/orders", "/users", "/orders", "/health", "/orders", "/users");

    Map.Entry<String, Long> top = paths.stream()
        .collect(Collectors.groupingBy(p -> p, Collectors.counting()))
        .entrySet().stream()
        .max(Map.Entry.comparingByValue())
        .orElseThrow();
    IO.println(top.getKey() + " was requested " + top.getValue() + " times");
}
// Output:
// /orders was requested 3 times

Events per day versus unique users per day

counting() counts every event, including repeats by the same user. Collecting the user ids into a set first and taking its size gives the distinct count, the stream form of COUNT(DISTINCT user_id).

Example · java
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

record Event(String day, String userId) {}

void main() {
    List<Event> events = List.of(
        new Event("2026-03-01", "u1"), new Event("2026-03-01", "u1"),
        new Event("2026-03-01", "u2"), new Event("2026-03-02", "u3"));

    Map<String, Long> eventsPerDay = events.stream()
        .collect(Collectors.groupingBy(Event::day, TreeMap::new, Collectors.counting()));
    Map<String, Integer> usersPerDay = events.stream()
        .collect(Collectors.groupingBy(Event::day, TreeMap::new,
            Collectors.collectingAndThen(
                Collectors.mapping(Event::userId, Collectors.toSet()), Set::size)));

    IO.println("events: " + eventsPerDay);
    IO.println("users:  " + usersPerDay);
}
// Output:
// events: {2026-03-01=3, 2026-03-02=1}
// users:  {2026-03-01=2, 2026-03-02=1}

Discussion

  • Be the first to comment on this lesson.