Lesson Completion
Back to course

Stream API Basics: Declarative Data Processing

Beginner
12 minutes4.6Java

1. The Hook (The "Byte-Sized" Intro)

  • In a Nutshell: Stream API processes data declaratively (what, not how). Not a data structure—sequence of elements from source (collection, array).
  • Pipeline: source → intermediate ops (filter, map) → terminal op (collect, forEach).
  • Lazy evaluation: Intermediate ops don't execute until terminal op.
  • Immutable: Stream ops don't modify source.
  • One-shot: Can't reuse stream after terminal op.
  • Parallel: .parallelStream() for multi-core.
  • Key: Replace imperative for-loops with functional pipelines!

Think of assembly line. Traditional loop = worker does everything (fetch part, inspect, assemble, pack). Stream = specialized stations (filter station, transform station, collect station). Each station does one thing. Declarative, composable, parallel!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy

  • Source: Raw materials warehouse
  • Intermediate operations: Processing stations (filter defects, paint, assemble)
  • Terminal operation: Shipping dock (final collection)
  • Lazy evaluation: Stations idle until truck arrives (terminal op triggers)

Stream Pipeline

graph LR A[Collection] -->|stream| B[Stream Source] B -->|filter| C[Intermediate] C -->|map| D[Intermediate] D -->|sorted| E[Intermediate] E -->|collect| F[Terminal] F --> G[Result] style B fill:#F57C00 style C fill:#2E7D32 style D fill:#2E7D32 style E fill:#2E7D32 style F fill:#C2185B

3. Technical Mastery (The "Deep Dive")

Stream Characteristics

FeatureDescription
Not data structureDoesn't store elements
FunctionalDoesn't modify source
LazyIntermediate ops deferred
One-shotCan't reuse after terminal op
Possibly unboundedStream.iterate() infinite
Parallelizable.parallelStream()

4. Interactive & Applied Code

java
import java.util.*; import java.util.stream.*; public class StreamBasicsDemo { public static void main(String[] args) { demonstrateCreatingStreams(); demonstrateLazyEvaluation(); demonstratePipeline(); demonstrateParallel(); } // Creating streams static void demonstrateCreatingStreams() { System.out.println("=== CREATING STREAMS ==="); // From collection List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream1 = list.stream(); // From array String[] array = {"x", "y", "z"}; Stream<String> stream2 = Arrays.stream(array); // From values Stream<String> stream3 = Stream.of("1", "2", "3"); // Empty stream Stream<String> empty = Stream.empty(); // Infinite streams Stream<Integer> infinite = Stream.iterate(0, n -> n + 1); // 0,1,2,3... Stream<Double> randoms = Stream.generate(Math::random); // Primitive streams IntStream ints = IntStream.range(1, 5); // 1,2,3,4 ints.forEach(System.out::println); } // Lazy evaluation static void demonstrateLazyEvaluation() { System.out.println("\n=== LAZY EVALUATION ==="); List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); System.out.println("Creating stream pipeline..."); Stream<Integer> stream = numbers.stream() .filter(n -> { System.out.println(" filtering: " + n); return n % 2 == 0; }) .map(n -> { System.out.println(" mapping: " + n); return n * n; }); System.out.println("Pipeline created, but not executed yet!"); System.out.println("Calling terminal operation..."); List<Integer> result = stream.collect(Collectors.toList()); System.out.println("Result: " + result); } // Complete pipeline example static void demonstratePipeline() { System.out.println("\n=== STREAM PIPELINE ==="); List<String> words = Arrays.asList( "apple", "banana", "cherry", "date", "elderberry", "fig", "grape" ); // ❌ BEFORE: Imperative (verbose) List<String> result1 = new ArrayList<>(); for (String word : words) { if (word.length() > 5) { String upper = word.toUpperCase(); result1.add(upper); } } System.out.println("Imperative: " + result1); // ✅ AFTER: Declarative (concise) List<String> result2 = words.stream() .filter(w -> w.length() > 5) // Keep long words .map(String::toUpperCase) // Transform to uppercase .collect(Collectors.toList()); // Collect to list System.out.println("Declarative: " + result2); } // Parallel streams static void demonstrateParallel() { System.out.println("\n=== PARALLEL STREAMS ==="); List<Integer> numbers = IntStream.rangeClosed(1, 1000) .boxed() .collect(Collectors.toList()); // Sequential long start = System.currentTimeMillis(); long sum1 = numbers.stream() .mapToLong(Integer::longValue) .sum(); long seq = System.currentTimeMillis() - start; // Parallel start = System.currentTimeMillis(); long sum2 = numbers.parallelStream() .mapToLong(Integer::longValue) .sum(); long par = System.currentTimeMillis() - start; System.out.println("Sequential: " + seq + "ms"); System.out.println("Parallel: " + par + "ms"); System.out.println("Speedup: " + (seq / (double) par) + "x"); } }

5. The Comparison & Decision Layer

Imperative LoopStream API
How to do itWhat to do
Mutable stateImmutable
Sequential onlyParallel easily
VerboseConcise
Error-proneSafer

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Why can't you reuse a stream?" Answer: Streams are one-shot—terminal op consumes stream!

java
Stream<String> stream = list.stream(); stream.forEach(System.out::println); // ✅ OK: first use stream.forEach(System.out::println); // ❌ ERROR: stream already used! // IllegalStateException: stream has already been operated upon or closed // Solution: Create new stream list.stream().forEach(System.out::println); list.stream().count(); // ✅ New stream

Pro-Tip: Sequential vs Parallel:

java
// Sequential (default) list.stream().filter(...).collect(...); // Parallel (multi-core) list.parallelStream().filter(...).collect(...); // When to use parallel? // ✅ DO: Large data (10K+ elements), CPU-intensive ops // ❌ DON'T: Small data, I/O ops, stateful operations

Topics Covered

Java Fundamentals

Tags

#java#programming#beginner-friendly

Last Updated

2025-02-01