Back to Collectors
Q5Question 5 of 21

summaryStatistics(): min/max/sum/average/count in one pass

Key Concepts
  • summaryStatistics() on IntStream/LongStream/DoubleStream computes count, sum, min, max, and average in ONE pass
  • Returns IntSummaryStatistics/LongSummaryStatistics/DoubleSummaryStatistics with getter methods for each statistic
  • Available as downstream collector: Collectors.summarizingInt/Long/Double for per-group statistics
  • Five times more efficient than computing each statistic separately (one pass vs five passes)
  • For empty streams: min returns MAX_VALUE, max returns MIN_VALUE (conventions for primitive types)
  • Use mapToInt/mapToLong/mapToDouble to convert object streams to primitive streams before calling summaryStatistics

Definition

summaryStatistics() computes count, sum, min, max, and average in a single pass, returning an IntSummaryStatistics (or Long/Double variant) with all five values.
  • summaryStatistics() on IntStream, LongStream, or DoubleStream returns a statistics object with count, sum, min, max, and average.
  • Single pass: all five statistics are computed in one traversal of the data, not five separate passes.
  • As downstream collector: Collectors.summarizingInt(mapper) computes per-group statistics within groupingBy.
  • mapToInt/Long/Double converts an object stream to a primitive stream before calling summaryStatistics.
  • For empty streams: min returns Integer.MAX_VALUE, max returns Integer.MIN_VALUE, average returns 0.0, count and sum return 0.

At National Bank of India, the branch dashboard needs five numbers for account balances: how many accounts, total deposits, smallest balance, largest balance, and average balance. Instead of scanning the ledger five times (once per statistic), summaryStatistics scans once and fills in all five numbers on the dashboard.

Understanding the Concepts

How do you use summaryStatistics?

JAVA
package com.bank.analytics;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

public class SummaryStatsDemo {
    record Account(String id, String branch, BigDecimal balance) {}

    public static void main(String[] args) {
        List<Account> accounts = List.of(
            new Account("SAV2001", "MUMBAI",  new BigDecimal("50000")),
            new Account("SAV2002", "MUMBAI",  new BigDecimal("150000")),
            new Account("SAV2003", "DELHI",   new BigDecimal("75000")),
            new Account("SAV2004", "DELHI",   new BigDecimal("120000")),
            new Account("SAV2005", "CHENNAI", new BigDecimal("200000"))
        );

        // Summary statistics for all accounts
        DoubleSummaryStatistics stats = accounts.stream()
            .mapToDouble(a -> a.balance().doubleValue())
            .summaryStatistics();

        System.out.println("Count:   " + stats.getCount());
        System.out.println("Sum:     Rs." + stats.getSum());
        System.out.println("Min:     Rs." + stats.getMin());
        System.out.println("Max:     Rs." + stats.getMax());
        System.out.printf("Average: Rs.%.2f%n", stats.getAverage());
    }
}
TEXT
Count:   5
Sum:     Rs.595000.0
Min:     Rs.50000.0
Max:     Rs.200000.0
Average: Rs.119000.00

How do you get per-group statistics?

JAVA
// Statistics per branch using summarizingDouble downstream
Map<String, DoubleSummaryStatistics> statsByBranch = accounts.stream()
    .collect(Collectors.groupingBy(
        Account::branch,
        Collectors.summarizingDouble(a -> a.balance().doubleValue())));

statsByBranch.forEach((branch, s) ->
    System.out.printf("%s: count=%d, avg=Rs.%.0f, min=Rs.%.0f, max=Rs.%.0f%n",
        branch, s.getCount(), s.getAverage(), s.getMin(), s.getMax()));
TEXT
MUMBAI: count=2, avg=Rs.100000, min=Rs.50000, max=Rs.150000
DELHI: count=2, avg=Rs.97500, min=Rs.75000, max=Rs.120000
CHENNAI: count=1, avg=Rs.200000, min=Rs.200000, max=Rs.200000

Why is one pass better than five?

JAVA
// WRONG: five separate passes
long count = accounts.stream().count();
double sum = accounts.stream().mapToDouble(a -> a.balance().doubleValue()).sum();
double min = accounts.stream().mapToDouble(a -> a.balance().doubleValue()).min().orElse(0);
double max = accounts.stream().mapToDouble(a -> a.balance().doubleValue()).max().orElse(0);
double avg = accounts.stream().mapToDouble(a -> a.balance().doubleValue()).average().orElse(0);
// 5 passes! For 1 million accounts, that's 5 million iterations.

// CORRECT: one pass
DoubleSummaryStatistics stats = accounts.stream()
    .mapToDouble(a -> a.balance().doubleValue())
    .summaryStatistics();
// 1 pass! For 1 million accounts, that's 1 million iterations.

What about IntSummaryStatistics vs DoubleSummaryStatistics?

| Type | Use When | Precision |
|------|---------|----------|
| IntSummaryStatistics | Integer values (age, count) | Exact |
| LongSummaryStatistics | Long values (timestamps, large counts) | Exact |
| DoubleSummaryStatistics | Double values (salary, balance) | Approximate |

For BigDecimal amounts, DoubleSummaryStatistics loses precision. Use a custom collector for exact BigDecimal statistics.

What are common mistakes?

WRONG: Calling summaryStatistics on an object stream without mapToInt/Double.

JAVA
// WRONG: Stream<Account> has no summaryStatistics()
accounts.stream().summaryStatistics();  // COMPILE ERROR

// CORRECT: convert to primitive stream first
accounts.stream().mapToDouble(a -> a.balance().doubleValue()).summaryStatistics();

What should a senior engineer know?

  • DoubleSummaryStatistics uses a compensated summation algorithm internally to reduce floating-point rounding errors.
  • For exact BigDecimal statistics, write a custom collector that accumulates BigDecimal sum, count, min, and max.
  • summaryStatistics() works correctly with parallel streams because IntSummaryStatistics has a combine() method.
  • The empty-stream defaults (min = MAX_VALUE, max = MIN_VALUE) follow the mathematical convention for identity elements of min/max operations.

Visual Overview

DIAGRAM
  accounts stream
       │
       ▼
  mapToDouble(balance)
  [50000, 150000, 75000, 120000, 200000]
       │
       ▼
  summaryStatistics() ── ONE PASS
       │
       ▼
  DoubleSummaryStatistics {
    count   = 5
    sum     = 595000.0
    min     = 50000.0
    max     = 200000.0
    average = 119000.0
  }

Key Takeaway

summaryStatistics computes count, sum, min, max, and average in a SINGLE PASS through the stream. This is five times more efficient than computing each statistic separately. It is available on IntStream, LongStream, and DoubleStream via .summaryStatistics(), and as a downstream collector Collectors.summarizingInt/Long/Double for per-group statistics within groupingBy.

At National Bank of India, the branch dashboard needs five numbers for account balances. Instead of scanning the ledger five times, summaryStatistics scans once and provides all five in one DoubleSummaryStatistics object.

For empty streams, min returns MAX_VALUE and max returns MIN_VALUE by convention. For exact BigDecimal statistics, write a custom collector.

Remember one sentence: summaryStatistics gives count, sum, min, max, and average in one pass instead of five.

---

Share
Use ← → arrow keys to navigate