Study guide

8.4 Primitive Streams and Parallel Stream Processing

Oracle Java SE 21 Developer 1Z0-830

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Java Basics, Primitive Types, and Operators

1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)

2 Chapter 2: Controlling Program Flow & Pattern Matching

2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements

3 Chapter 3: Object-Oriented Design & Class Architecture

3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs

4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types

4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness

5 Chapter 5: Exception Handling and Annotations

5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

6 Chapter 6: Generics and the Collections Framework

6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class

7 Chapter 7: Functional Programming & Lambda Expressions

7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional

8 Chapter 8: The Stream API and Data Pipelines

8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing

9 Chapter 9: Concurrency, Multithreading, and Virtual Threads

9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)

10 Chapter 10: The Java Platform Module System (JPMS)

10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink

11 Chapter 11: Java I/O, NIO.2, and Object Serialization

11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization

12 Chapter 12: Implementing Localization and Internationalization

12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages

8.4 Primitive Streams and Parallel Stream Processing

Key Takeaways Explain This Topic AI

Primitive streams (IntStream, LongStream, DoubleStream) avoid object boxing overhead and provide specialized numerical operations including sum, average, range, and summaryStatistics.

Bridging between object and primitive streams requires explicit mapping methods (mapToInt, mapToObj, boxed) and specialized functional interfaces (IntFunction, ToIntFunction, IntUnaryOperator).

Parallel streams split data across threads in ForkJoinPool.commonPool() using Spliterator decomposition, maximizing throughput for large, CPU-intensive, stateless operations.

Parallel stream operations must adhere strictly to non-interference and statelessness; mutating shared mutable state from within parallel operations causes race conditions and data corruption.

Last updated: September 2026

Primitive Streams and Parallel Stream Processing

While reference streams ( Stream<T> ) provide a uniform object-oriented abstraction, processing millions of numeric values through wrapper types (like Integer , Long , or Double ) incurs substantial memory allocation and boxing/unboxing overhead. To achieve near-native performance, Java provides primitive specialized streams : IntStream , LongStream , and DoubleStream .

Furthermore, Java's Stream API allows seamless parallelization across multi-core processors via parallelStream() and .parallel() . For the Java SE 21 Developer (1Z0-830) exam, candidates must understand primitive creation, conversions, numerical aggregations, Spliterator mechanics, and the strict concurrency rules governing parallel stream execution.

1. Primitive Stream Architecture

Java provides three specialized primitive stream interfaces in java.util.stream :

IntStream : Represents a sequence of primitive int values (also handles byte , short , and char ).

LongStream : Represents a sequence of primitive long values.

DoubleStream : Represents a sequence of primitive double values (also handles float ). ┌────────────────────────┐

  • │ BaseStream<T, S> │
  • └───────────┬────────────┘
  • ┌──────────────────────┼──────────────────────┐
  • │ │ │
  • ┌─────────▼────────┐ ┌─────────▼────────┐ ┌─────────▼────────┐
  • │ IntStream │ │ LongStream │ │ DoubleStream │
  • └──────────────────┘ └──────────────────┘ └──────────────────┘
  • Creating Primitive Streams // 1. Static of() factory
  • IntStream is1 = IntStream.of(1, 2, 3, 4, 5);
  • DoubleStream ds1 = DoubleStream.of(1.5, 2.5, 3.5);
  • // 2. Numeric Ranges (IntStream & LongStream only - DoubleStream has no range methods)
  • IntStream exclusive = IntStream.range(1, 5); // 1, 2, 3, 4 (5 is excluded)
  • IntStream inclusive = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5 (5 is included)
  • // 3. From Arrays
  • int[] nums = {10, 20, 30};
  • IntStream is2 = Arrays.stream(nums);
  • // 4. Random Primitives
  • IntStream randomInts = new Random().ints(5, 1, 100); // 5 ints between 1 and 99
  • // 5. From CharSequence / String
  • IntStream chars = "Java 21".chars(); // IntStream of UTF-16 char codes
  • 2. Primitive Operations & Summary Statistics

Primitive streams include specialized numeric terminal operations not found on generic Stream<T> : IntStream stream = IntStream.of(10, 20, 30, 40, 50);

  • // Direct numeric reduction (no Comparator needed!)
  • int sum = IntStream.of(1, 2, 3).sum(); // 6
  • OptionalDouble avg = IntStream.of(10, 20, 30).average(); // OptionalDouble[20.0]
  • OptionalInt min = IntStream.of(10, 20, 30).min(); // OptionalInt[10]
  • OptionalInt max = IntStream.of(10, 20, 30).max(); // OptionalInt[30]
  • // Single-pass Summary Statistics
  • IntSummaryStatistics stats = IntStream.of(10, 20, 30, 40, 50).summaryStatistics();
  • System.out.println("Count: " + stats.getCount()); // 5
  • System.out.println("Sum: " + stats.getSum()); // 150 (returns long)
  • System.out.println("Min: " + stats.getMin()); // 10
  • System.out.println("Max: " + stats.getMax()); // 50
  • System.out.println("Average: " + stats.getAverage()); // 30.0 (returns double)
  • Primitive Optional Types: OptionalInt , OptionalLong , OptionalDouble

Primitive optionals avoid boxing the contained numeric value. Note the method names for retrieving values: OptionalInt optInt = IntStream.empty().max();

// Value extraction methods:

// optInt.getAsInt(); // Throws NoSuchElementException on empty!

  • int val = optInt.orElse(0); // 0
  • int computed = optInt.orElseGet(() -> 100);

Warning

OptionalInt does NOT possess a .get() method. Calling .get() on an OptionalInt causes a compile-time error ; you must call .getAsInt() .

3. Conversions Between Object and Primitive Streams

Navigating between Stream<T> and primitive streams requires specific transformation methods: List<String> words = List.of("Java", "SE", "21");

  • // Object Stream -> Primitive Stream
  • IntStream lengths = words.stream().mapToInt(String::length); // ToIntFunction
  • DoubleStream dStream = words.stream().mapToDouble(s -> s.length() * 1.5);
  • LongStream lStream = words.stream().mapToLong(s -> (long) s.length());
  • // Primitive Stream -> Object Stream (Boxed vs mapToObj)
  • Stream<Integer> boxedStream = IntStream.rangeClosed(1, 5).boxed(); // Wraps int into Integer
  • Stream<String> mappedObj = IntStream.rangeClosed(1, 3)
  • .mapToObj(n -> "ID_" + n); // ["ID_1", "ID_2", "ID_3"]
  • // Primitive -> Primitive Conversions
  • DoubleStream fromIntToDouble = IntStream.of(1, 2, 3).asDoubleStream();
  • LongStream fromIntToLong = IntStream.of(1, 2, 3).asLongStream();
  • Transformation Summary Matrix
  • Source Stream Target Stream Transformation Method
  • Stream<T> IntStream stream.mapToInt(ToIntFunction<T>)
  • Stream<T> LongStream stream.mapToLong(ToLongFunction<T>)
  • Stream<T> DoubleStream stream.mapToDouble(ToDoubleFunction<T>)
  • IntStream Stream<Integer> intStream.boxed()
  • IntStream Stream<U> intStream.mapToObj(IntFunction<U>)
  • IntStream DoubleStream intStream.asDoubleStream()
  • IntStream LongStream intStream.asLongStream()
  • 4. Parallel Streams Architecture & Execution

Parallel streams decompose stream workloads across available CPU cores using the Fork/Join Framework ( ForkJoinPool.commonPool() ). // Creating a parallel stream

List<Integer> data = List.of(1, 2, 3, 4, 5, 6, 7, 8);

  • Stream<Integer> p1 = data.parallelStream();
  • Stream<Integer> p2 = data.stream().parallel();
  • // Converting back to sequential
  • Stream<Integer> seq = p2.sequential();
  • boolean isParallel = p1.isParallel(); // true
  • How Fork/Join Decomposition Works

Splitting ( trySplit ) : The source is recursively split into sub-ranges by its Spliterator .

Execution : Leaf subtasks are executed concurrently on worker threads in ForkJoinPool.commonPool() .

Combining : Sub-results are combined up the fork-join tree using associative combiners.

5. Spliterator Mechanics and Characteristics

The Spliterator<T> ("splitable iterator") is the core engine backing all streams. It provides four essential methods:

boolean tryAdvance(Consumer<? super T> action) : Steps forward one element (like Iterator.next() ).

Spliterator<T> trySplit() : Partitions off a portion of elements to be processed by another thread in parallel.

long estimateSize() : Returns the estimated remaining element count.

int characteristics() : Returns a bitmask of stream characteristics.

Spliterator Characteristics Matrix

Characteristic Bit Constant Impact on Stream Optimization

ORDERED Spliterator.ORDERED Stream possesses an encounter order; operations like findFirst() , limit() must preserve sequence.

DISTINCT Spliterator.DISTINCT Stream contains no duplicate elements; distinct() is optimized away as a no-op.

SORTED Spliterator.SORTED Stream is already sorted; sorted() is a no-op.

SIZED Spliterator.SIZED Exact size is known before traversal; collectors can pre-allocate array buffers.

NONNULL Spliterator.NONNULL Guaranteed to contain no null elements.

IMMUTABLE Spliterator.IMMUTABLE Data source cannot be structurally modified during traversal.

CONCURRENT Spliterator.CONCURRENT Data source can be safely modified concurrently without external synchronization.

SUBSIZED Spliterator.SUBSIZED All splits produced by trySplit() will also be SIZED .

6. Stateful vs. Stateless Operations in Parallel Processing

Stateless Operations ( filter , map , flatMap ) : Subtasks operate completely independently across threads with zero synchronization overhead, achieving near-linear scaling.

Stateful Operations ( sorted , distinct , limit , skip ) : Require barrier synchronization and cross-thread communication. In parallel pipelines, sorted() must buffer elements across all worker threads before merging, often running slower than sequential execution !

The unordered() Optimization

When processing a parallel stream where encounter order does not matter, calling .unordered() allows distinct() , limit() , and groupingByConcurrent() to execute dramatically faster without maintaining partition ordering constraints: List<Integer> distinctItems = largeList.parallelStream()

  • .unordered() // Removes ordering constraint
  • .distinct() // Much faster in parallel
  • .toList();
  • 7. Concurrency Hazards, Non-Interference, and Thread Safety

Caution

The Parallel Mutation Anti-Pattern : Never mutate shared state from inside a stream pipeline (e.g., inside forEach or map ). Parallel streams execute across multiple concurrent worker threads; accessing non-thread-safe containers without synchronization causes race conditions, lost updates, and corrupted data. // DANGEROUS / BUGGY CODE: Race condition on ArrayList

  • List<Integer> unsafeResult = new ArrayList<>();
  • IntStream.rangeClosed(1, 1000)
  • .parallel()
  • .filter(n -> n % 2 == 0)

.forEach(unsafeResult::add); // CRASH or corrupted size < 500!

  • // CORRECT & THREAD-SAFE APPROACH: Use collect() or reduce()
  • List<Integer> safeResult = IntStream.rangeClosed(1, 1000)
  • .parallel()
  • .filter(n -> n % 2 == 0)
  • .boxed()
  • .toList(); // Perfectly thread-safe and deterministic count of 500

Loading diagram... Primitive Conversions and Parallel ForkJoinPool Spliterator Decomposition