Java Stream API: Pipelines, Filtering, Mapping, Aggregation & Parallel Streams

โ˜• Java 21 LTS ๐ŸŸข Lesson 50 ๐Ÿ“‚ Phase 19: Stream API & Pipelines ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this in-depth guide: Stream ante enti? ยท Collection vs Stream ยท Intermediate (filter, map, sorted, distinct, limit, skip) ยท Terminal (forEach, reduce, count, min, max) ยท Collectors (toList, groupingBy, partitioningBy) ยท Parallel Streams

Welcome to Phase 19: Java Stream API & Pipelines Masterclass! Introduced in Java 8 and continuously enhanced up to Java 21 LTS, the Stream API (java.util.stream) is one of the most powerful features in modern Java. A Stream is not a data structure โ€” it does not store data. Instead, it is a declarative computational pipeline that processes data sequences from Collections, Arrays, or I/O channels with zero memory mutation, lazy evaluation, and seamless parallel multithreading capability. In this comprehensive in-depth guide, you will master every core stream method, intermediate vs terminal execution models, complex grouping aggregators, and parallel stream performance.

1Stream Ante Enti? Collections vs Streams (Core Architecture)

Java lo Stream ante data items เฐฏเฑŠเฐ•เฑเฐ• continuous sequence paina functional transformations (filter, transform, aggregate) perform chese computation pipeline. Dheeni valla traditional nested for-loops and mutable temporary lists completely eliminate avthayi:

๐Ÿ’ก Collections vs Streams โ€” Architectural Differences

FeatureJava Collection (e.g. ArrayList)Java Stream (java.util.stream)
Primary RoleData Storage: Holds elements in Heap memory.Computation: Processes elements on-demand.
Iteration ModelExternal Iteration: User writes for (int i...) loop explicitly.Internal Iteration: Stream library manages iteration internally.
Execution TimingEager: Elements are created & stored immediately.Lazy: No work is executed until a Terminal Operation is invoked!
ReusabilityReusable multiple times.Consumable Once: Once terminal op runs, the stream is closed!
Data MutationDirectly mutates backing collection (e.g. add(), remove()).Non-mutating: Never modifies original source data.
The 3-Stage Stream Pipeline Architecture:
[SOURCE] [INTERMEDIATE OPS (Lazy)] [TERMINAL OP (Eager)]
List<Integer> โ”€โ”€โ–บ .filter(n -> n % 2 == 0) โ”€โ”€โ–บ .map(n -> n * 2) โ”€โ”€โ–บ .forEach(System.out::println)
(Numbers) (Filters Odd numbers) (Doubles evens) (Prints & Executes Pipeline!)
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Chain of Lazy Transformations
2How to Create Streams in Java

Java lo streams ni multiple sources nunchi construct cheyyavachu:

Java 21 โ€” Stream Creation Sources
import java.util.*;
import java.util.stream.*;

public class StreamSourcesDemo {
    public static void main(String[] args) {
        // 1. From Collection
        List<String> list = List.of("Java", "Spring", "Docker");
        Stream<String> s1 = list.stream();

        // 2. From Array
        String[] arr = {"A", "B", "C"};
        Stream<String> s2 = Arrays.stream(arr);

        // 3. From Static Values
        Stream<Integer> s3 = Stream.of(10, 20, 30);

        // 4. Primitive IntStream (Zero Autoboxing Overhead!)
        IntStream intStream = IntStream.rangeClosed(1, 5); // 1, 2, 3, 4, 5

        // 5. Infinite Streams with limit
        Stream<Double> randomNumbers = Stream.generate(Math::random).limit(3);
    }
}
3Intermediate Operations Deep Dive (Lazy Transformers)

Intermediate Operations eppudu kotha Stream ni return chesthayi mariyu Lazy ga untayi โ€” ante Terminal operation call ayye daka intermediate operations okkati kuda execute avvadhu:

MethodArgumentDescription & Purpose
filter(Predicate)n -> n > 50Selects only elements matching the boolean condition.
map(Function)s -> s.toUpperCase()Transforms each element into another object/value.
sorted() / sorted(Comparator)(a, b) -> b - aSorts elements in natural or custom comparator order.
distinct()none (uses .equals())Eliminates all duplicate elements.
limit(n)long maxSizeTruncates stream to at most n elements.
skip(n)long nDiscards the first n elements (ideal for pagination!).
Java 21 โ€” Intermediate Operations Demo
import java.util.List;

public class IntermediateOpsDemo {
    public static void main(String[] args) {
        List<Integer> rawScores = List.of(85, 42, 90, 85, 30, 95, 78, 90, 60);

        System.out.println("Top 3 Unique Passing Scores (>= 70):");
        rawScores.stream()
                 .filter(score -> score >= 70)      // Filter passing grades
                 .distinct()                        // Remove duplicates (85, 90)
                 .sorted((a, b) -> b - a)           // Sort DESCENDING
                 .limit(3)                          // Pick top 3
                 .forEach(System.out::println);     // Terminal Op! (95, 90, 85)
    }
}
4Terminal Operations: forEach, count, min, max & reduce()

Terminal Operations stream pipeline execution ni trigger chesi non-stream result ni (e.g. single number, List, Map, boolean) return chesthayi:

Java 21 โ€” Reductions with reduce(), count(), min() & max()
import java.util.List;
import java.util.Optional;

public class ReductionsDemo {
    public static void main(String[] args) {
        List<Integer> cartPrices = List.of(1200, 450, 3000, 850, 150);

        // 1. count()
        long totalItems = cartPrices.stream().count();
        System.out.println("Total Items: " + totalItems);

        // 2. min() & max() with Optional
        Optional<Integer> cheapest = cartPrices.stream().min(Integer::compareTo);
        Optional<Integer> mostExpensive = cartPrices.stream().max(Integer::compareTo);
        System.out.println("Cheapest: Rs." + cheapest.orElse(0));
        System.out.println("Most Expensive: Rs." + mostExpensive.orElse(0));

        // 3. reduce(identity, accumulator) -> Calculate Total Sum
        int totalPrice = cartPrices.stream().reduce(0, (sum, price) -> sum + price);
        System.out.println("Total Cart Value: Rs." + totalPrice);
    }
}
5Data Aggregation with collect(): groupingBy & partitioningBy โญ

Enterprise applications lo database query results ni memory lo group cheyyadaniki Collectors.groupingBy() mariyu Collectors.partitioningBy() vadathamu:

Java 21 โ€” Grouping & Partitioning Masterclass
import java.util.*;
import java.util.stream.Collectors;

class Employee {
    private String name;
    private String department;
    private double salary;

    public Employee(String name, String dept, double sal) {
        this.name = name;
        this.department = dept;
        this.salary = sal;
    }

    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }

    @Override
    public String toString() { return name + " (Rs." + salary + ")"; }
}

public class CollectorsGroupingDemo {
    public static void main(String[] args) {
        List<Employee> employees = List.of(
            new Employee("Ravi", "Engineering", 85000),
            new Employee("Sneha", "HR", 60000),
            new Employee("Kiran", "Engineering", 110000),
            new Employee("Anu", "Marketing", 75000),
            new Employee("Bhavna", "HR", 65000)
        );

        // 1. Collect to Modern Immutable List (Java 16+ Stream.toList())
        List<String> engNames = employees.stream()
            .filter(e -> e.getDepartment().equals("Engineering"))
            .map(Employee::getName)
            .toList();
        System.out.println("Engineering Team: " + engNames);

        // 2. groupingBy: Group employees by Department
        Map<String, List<Employee>> byDept = employees.stream()
            .collect(Collectors.groupingBy(Employee::getDepartment));
        System.out.println("\nEmployees by Department: " + byDept);

        // 3. partitioningBy: Split into High Earners (>= 80k) and Regular Earners
        Map<Boolean, List<Employee>> highEarners = employees.stream()
            .collect(Collectors.partitioningBy(e -> e.getSalary() >= 80000));
        System.out.println("\nHigh Earners (>=80k): " + highEarners.get(true));
    }
}
6Sequential vs Parallel Streams (Multi-Core Processing)

โšก How Parallel Streams Work Under the Hood

collection.parallelStream() data sequence ni multiple chunks ga divide chesi JVM เฐฏเฑŠเฐ•เฑเฐ• common ForkJoinPool.commonPool() worker threads meedha parallel ga concurrently process chesthundhi.
โš ๏ธ When to use: Large CPU-intensive datasets (> 100,000 elements) with stateless independent operations.
โš ๏ธ When to avoid: Small datasets (thread coordination overhead makes it slower than sequential), or when operations involve shared mutable state or blocking I/O!

Java 21 โ€” Parallel Stream Benchmark
import java.util.stream.LongStream;

public class ParallelStreamDemo {
    public static void main(String[] args) {
        long limit = 10_000_000L;

        // Sequential Stream Sum
        long startSeq = System.currentTimeMillis();
        long sumSeq = LongStream.rangeClosed(1, limit).sum();
        long endSeq = System.currentTimeMillis();
        System.out.println("Sequential Sum: " + sumSeq + " (Time: " + (endSeq - startSeq) + " ms)");

        // Parallel Multi-Core Stream Sum
        long startPar = System.currentTimeMillis();
        long sumPar = LongStream.rangeClosed(1, limit).parallel().sum();
        long endPar = System.currentTimeMillis();
        System.out.println("Parallel Sum:   " + sumPar + " (Time: " + (endPar - startPar) + " ms)");
    }
}
๐Ÿ’ป Try It Yourself โ€” User Curriculum Code Example

Run this stream filter and transform pipeline in our online Java compiler:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(10, 15, 20, 25, 30);

        numbers.stream()
               .filter(number -> number % 2 == 0)
               .map(number -> number * 2)
               .forEach(System.out::println);
    }
}
Open in Online Java Compiler โ†’
OC
Written and reviewed by Our Compiler Technical Team ยท Updated for Java 21 LTS (2026 Edition)