Study guide

8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

Key Takeaways Explain This Topic AI

Primitive streams (IntStream, LongStream, DoubleStream) eliminate boxing and unboxing overhead, bypassing object allocation and pointer indirection for high-throughput numeric workloads.

Numeric ranges are generated via range(start, end) (exclusive upper bound) and rangeClosed(start, end) (inclusive upper bound); DoubleStream does not support range generation.

Conversions between object streams and primitive streams are achieved using mapToInt(), mapToLong(), mapToDouble(), boxed(), and mapToObj().

Primitive stream terminal reductions include sum() (returning primitive number, 0 on empty), average() (returning OptionalDouble across all primitive streams), and min()/max() (returning primitive optionals).

summaryStatistics() calculates count, sum, min, average, and max in a single traversal, returning an IntSummaryStatistics, LongSummaryStatistics, or DoubleSummaryStatistics instance.

Last updated: October 2026

8.4 Primitive Streams ( IntStream , LongStream , DoubleStream ) and Numeric Statistics

While generic Stream<T> handles reference types seamlessly, using it for numeric computations involves boxing primitive numbers into wrapper objects ( Integer , Long , Double ). Each boxed object introduces 16 to 24 bytes of heap memory overhead, cache line pollution, and CPU cycles spent on autoboxing and unboxing. To eliminate these performance bottlenecks, the java.util.stream package provides three specialized primitive stream interfaces: IntStream , LongStream , and DoubleStream .

The Three Primitive Stream Types

Java provides primitive stream specializations for the three most common primitive numeric representations:

IntStream : 32-bit signed integers. Also handles narrower integral primitives ( byte , short , and char ) through implicit widening.

LongStream : 64-bit signed integers.

DoubleStream : 64-bit IEEE 754 floating-point numbers. Also handles 32-bit float primitives through implicit widening.

Note

Java does not provide ByteStream , ShortStream , CharStream , FloatStream , or BooleanStream . Integral types narrower than int are always processed as IntStream , float is processed as DoubleStream , and booleans are represented using predicates or standard reference streams.

Primitive Stream Creation and Numeric Ranges

Primitive streams can be instantiated using explicit values, arrays, generator functions, or contiguous numeric ranges:

  • 1. Explicit Values and Arrays IntStream s1 = IntStream.of(1, 2, 3, 4, 5);
  • IntStream s2 = IntStream.of(100); // Single element
  • LongStream s3 = LongStream.of(10L, 20L, 30L);
  • DoubleStream s4 = DoubleStream.of(1.5, 2.5, 3.5);
  • int[] numbers = { 10, 20, 30 };
  • IntStream s5 = Arrays.stream(numbers); // Array source
  • 2. Contiguous Ranges: range() vs. rangeClosed()

IntStream and LongStream provide static factory methods to generate sequential arithmetic progressions with step size 1:

range(startInclusive, endExclusive) : Increments from startInclusive up to endExclusive - 1 .

rangeClosed(startInclusive, endInclusive) : Increments from startInclusive up to and including endInclusive . // Generates 1, 2, 3, 4 (count = 4)

long countA = IntStream.range(1, 5).count();

  • // Generates 1, 2, 3, 4, 5 (count = 5)
  • long countB = IntStream.rangeClosed(1, 5).count();

Warning

The DoubleStream Range Absence Trap Neither range() nor rangeClosed() exists on DoubleStream ! Because floating-point numbers have an infinite number of real values between any two points and step sizes cannot default to 1 without precision ambiguity, DoubleStream.range(1.0, 5.0) does not exist and will fail compilation.

3. Iterators and Generators // Infinite even numbers: 0, 2, 4, 6, ...

IntStream evens = IntStream.iterate(0, n -> n + 2);

  • // Finite 3-arg iterate (Java 9+): 10, 20, 30, 40, 50
  • IntStream tens = IntStream.iterate(10, n -> n <= 50, n -> n + 10);
  • // Random numbers via IntSupplier
  • IntStream randomDice = IntStream.generate(() -> (int)(Math.random() * 6) + 1).limit(3);
  • Conversions: Boxing, Mapping, and Bridging

Transitioning between generic object streams and primitive streams requires dedicated bridge methods:

1. Object Stream to Primitive Stream

To convert a Stream<T> to a primitive stream, use the mapTo* family of intermediate operations:

  • mapToInt(ToIntFunction<? super T>) -> IntStream
  • mapToLong(ToLongFunction<? super T>) -> LongStream

mapToDouble(ToDoubleFunction<? super T>) -> DoubleStream List<String> items = List.of("apple", "banana", "pear");

  • IntStream lengths = items.stream().mapToInt(String::length);
  • 2. Primitive Stream to Object Stream

To convert a primitive stream back into an object stream, use boxed() or mapToObj() :

boxed() : Converts each primitive into its corresponding wrapper object ( IntStream -> Stream<Integer> ).

mapToObj(IntFunction<? extends U>) : Transforms each primitive into an arbitrary reference object U . IntStream numbers = IntStream.rangeClosed(1, 3);

  • // Boxed wrapper stream
  • Stream<Integer> boxedStream = numbers.boxed();
  • // Mapping to custom domain objects
  • Stream<String> labeled = IntStream.rangeClosed(1, 3)
  • .mapToObj(n -> "ID-" + n); // Stream<String>
  • 3. Widening Inter-Primitive Conversions

Primitive streams support implicit widening conversions without manual casting:

  • IntStream.asLongStream() -> LongStream
  • IntStream.asDoubleStream() -> DoubleStream

LongStream.asDoubleStream() -> DoubleStream DoubleStream ds = IntStream.range(1, 5).asDoubleStream(); // Yields 1.0, 2.0, 3.0, 4.0

Narrowing conversions (e.g., LongStream to IntStream or DoubleStream to IntStream ) are not provided as direct methods to prevent accidental precision loss; you must use explicit casts inside mapToInt(d -> (int) d) .

Specialized Reductions: sum() , average() , min() , max()

Primitive streams provide dedicated mathematical reduction methods that avoid the overhead of general reduce() and custom comparators:

Reduction Method Return Type Empty Stream Result Key Characteristics

sum() int / long / double 0 (or 0.0 ) Returns primitive directly; never returns an Optional !

average() OptionalDouble OptionalDouble.empty() Always returns OptionalDouble , even for IntStream !

min() OptionalInt / OptionalLong / OptionalDouble Optional*.empty() Returns specialized primitive optional.

max() OptionalInt / OptionalLong / OptionalDouble Optional*.empty() Returns specialized primitive optional. int sum = IntStream.of(10, 20, 30).sum(); // 60

  • OptionalDouble avg = IntStream.of(10, 20, 30).average(); // OptionalDouble[20.0]
  • OptionalInt max = IntStream.of(10, 20, 30).max(); // OptionalInt[30]

// Empty stream behavior:

  • int emptySum = IntStream.empty().sum(); // 0
  • OptionalDouble emptyAvg = IntStream.empty().average(); // OptionalDouble.empty
  • OptionalInt emptyMin = IntStream.empty().min(); // OptionalInt.empty

Warning

The sum() vs. average() Return Type Trap Notice the deliberate design asymmetry:

sum() returns a primitive value directly ( int , long , or double ). On an empty stream, sum() evaluates to 0 .

average() returns an OptionalDouble across all primitive streams ( IntStream.average() , LongStream.average() , and DoubleStream.average() ). An average of integers can produce a floating-point number, and an average over an empty stream is mathematically undefined (division by zero).

Multi-Metric Aggregation: summaryStatistics()

Because streams adhere to the single-use rule, executing min() , max() , and average() on the same stream would require reading and recreating the stream multiple times. To solve this, primitive streams provide the summaryStatistics() terminal operation.

summaryStatistics() consumes the stream in a single pass and computes count, sum, min, average, and max simultaneously: IntSummaryStatistics stats = IntStream.rangeClosed(1, 100).summaryStatistics();

  • System.out.println("Count: " + stats.getCount()); // 100
  • System.out.println("Sum: " + stats.getSum()); // 5050
  • System.out.println("Min: " + stats.getMin()); // 1
  • System.out.println("Average: " + stats.getAverage()); // 50.5
  • System.out.println("Max: " + stats.getMax()); // 100
  • Combining Statistics and Empty Stream Edge Cases

SummaryStatistics objects can also be combined across data partitions using combine() : IntSummaryStatistics stats1 = IntStream.rangeClosed(1, 50).summaryStatistics();

  • IntSummaryStatistics stats2 = IntStream.rangeClosed(51, 100).summaryStatistics();
  • stats1.combine(stats2); // Merges stats2 into stats1
  • System.out.println("Merged sum: " + stats1.getSum()); // 5050

Warning

summaryStatistics() on Empty Streams When summaryStatistics() is called on an empty stream, getCount() returns 0 , getSum() returns 0 , and getAverage() returns 0.0 . However, to preserve mathematical identity properties when combining statistics, getMin() returns Integer.MAX_VALUE (2147483647) and getMax() returns Integer.MIN_VALUE (-2147483648)!