What does summaryStatistics() return on a primitive stream, and why might you prefer it over calling sum(), min(), max(), and average() separately?
answer
- one pass -> count, sum, min, max, average
- stream is single-use; can't call sum() then min()
- returns IntSummaryStatistics / Long / Double
- empty: min=MAX_VALUE, max=MIN_VALUE, avg=0.0 (check getCount)
- Collectors.summarizingInt for object streams
basics
~20 ssummaryStatistics() returns one object (e.g. IntSummaryStatistics) holding the count, sum, min, max, and average computed in a single pass over the stream. Calling sum(), min(), max(), average() separately would consume the stream multiple times — but a stream can only be consumed once.
solid answer
~40 sOn IntStream/LongStream/DoubleStream, summaryStatistics() is a terminal operation returning an IntSummaryStatistics (or Long/Double variant) — an object exposing getCount(), getSum(), getMin(), getMax(), and getAverage(). The key reason to use it: a stream is consumed by exactly one terminal operation, so you cannot call sum() then min() on the same stream — the second throws IllegalStateException. To get several aggregates you'd otherwise rebuild the stream once per metric, iterating the data multiple times. summaryStatistics() computes all five in a single pass, which is both correct (no re-consumption) and efficient (one traversal). It also handles the empty-stream case sensibly: getMin() returns Integer.MAX_VALUE and getMax() returns Integer.MIN_VALUE, getCount() is 0, getAverage() is 0.0. For collecting from an object stream you can use Collectors.summarizingInt to get the same object.
code
java · 15 linesList<Integer> scores = List.of(40, 90, 70, 55);
IntSummaryStatistics stats = scores.stream()
.mapToInt(Integer::intValue)
.summaryStatistics(); // ONE pass
System.out.println(stats.getCount()); // 4
System.out.println(stats.getSum()); // 255
System.out.println(stats.getMin()); // 40
System.out.println(stats.getMax()); // 90
System.out.println(stats.getAverage()); // 63.75
// Empty-stream gotcha:
IntSummaryStatistics empty = IntStream.empty().summaryStatistics();
System.out.println(empty.getMin()); // 2147483647 (Integer.MAX_VALUE!)go deeper
Knows summaryStatistics() returns one object with count/sum/min/max/average from a single pass.
Explains why you can't call sum() then min() separately (single-use stream) and that summaryStatistics solves it in one traversal; knows the IntSummaryStatistics accessor methods.
Aware of the empty-stream sentinel values (min=MAX_VALUE), uses Collectors.summarizingInt on object streams, and knows when a single sum() is clearer.
Understands the combine()/identity design that makes the statistics object associative and parallel-friendly, and weighs one-pass aggregation in performance-sensitive pipelines.
## Background: a stream is single-use A Java **stream** is not a data structure you can re-read; it is a one-shot pipeline. Exactly **one terminal operation** may run on it. A terminal operation (e.g. `sum()`, `count()`, `collect()`) triggers the pipeline and **consumes** the stream. Trying a second terminal call on the same stream throws `IllegalStateException: stream has already been operated upon or closed`. This matters the moment you want **several aggregates** of the same numbers. You cannot write: ```java IntStream s = data.stream().mapToInt(...); int sum = s.sum(); // consumes s int max = s.max()...; // throws — s is already consumed ``` The workaround would be to rebuild the stream once per metric (`data.stream()...sum()`, `data.stream()...max()`, …), iterating the source **N times** — wasteful, and for an un-repeatable source (like an `Iterator`-backed stream) impossible. ## What summaryStatistics() gives you Each primitive stream offers a terminal op `summaryStatistics()` that returns a **statistics object**: - `IntStream` → `IntSummaryStatistics` - `LongStream` → `LongSummaryStatistics` - `DoubleStream` → `DoubleSummaryStatistics` That object holds five aggregates computed in **one traversal**, queried after the fact: - `getCount()` → number of elements (a `long`) - `getSum()` → total (a `long` for Int/Long, `double` for Double) - `getMin()` → smallest element - `getMax()` → largest element - `getAverage()` → arithmetic mean as a `double` So instead of multiple passes and multiple stream rebuilds, you do: ```java IntSummaryStatistics stats = data.stream().mapToInt(...).summaryStatistics(); stats.getCount(); stats.getSum(); stats.getMin(); stats.getMax(); stats.getAverage(); ``` ## Why prefer it 1. **Correctness:** it sidesteps the single-use limitation — all metrics from one terminal call. 2. **Efficiency:** one pass over the data instead of one pass per metric. For large or expensive-to-produce sources this is a meaningful win. 3. **Clarity:** the intent ('I want the stats of these numbers') is expressed once. ## How it behaves on an empty stream Unlike `min()`/`max()`/`average()` (which return an **empty Optional** — `OptionalInt`/`OptionalDouble` — when there are no elements), the summary object always returns concrete values: - `getCount()` = `0`, `getSum()` = `0` - `getMin()` = `Integer.MAX_VALUE` (the identity for a running minimum) - `getMax()` = `Integer.MIN_VALUE` (the identity for a running maximum) - `getAverage()` = `0.0` These 'sentinel' extremes for an empty stream are a known gotcha: `getMin()` on no data is **not** an error and **not** zero — it is the type's max value. Check `getCount() == 0` before trusting min/max. ## Getting the same object from an object stream If you have a `Stream<T>` rather than a primitive stream, the `Collectors` class provides `summarizingInt`/`summarizingLong`/`summarizingDouble`, which collect into the very same statistics types: ```java IntSummaryStatistics stats = people.stream() .collect(Collectors.summarizingInt(Person::age)); ``` You can also **merge** statistics objects with `combine(...)`, which is how parallel streams aggregate partial results — making the object naturally parallel-friendly. ## When NOT to bother If you truly need only one aggregate (just the sum), call `sum()` directly — it is clearer than fetching one field off a stats object. `summaryStatistics()` earns its place when you need **two or more** of count/sum/min/max/average together.
- What does getMin() return for an empty IntStream, and why?Integer.MAX_VALUE. The running-minimum accumulator is seeded with the largest possible int (the identity for min), so if no element ever updates it, that seed remains. Always guard with getCount() == 0 before using min/max on possibly-empty data.
- How do you get an IntSummaryStatistics from a Stream<Person> by age?people.stream().collect(Collectors.summarizingInt(Person::age)). The summarizingInt/Long/Double collectors produce the same statistics objects from an object stream without an explicit mapToInt.
saying these in an interview costs you the question
- Calling sum() and then min() on the same stream — the second throws IllegalStateException.
- Assuming getMin() returns 0 for an empty stream — it returns Integer.MAX_VALUE.
- Thinking summaryStatistics requires multiple passes — it computes everything in one traversal.
- Using it when you need only a single aggregate (sum() alone is clearer).