DEV Community

Vivek Nishant
Vivek Nishant

Posted on

Java Stream Notes

Java Streams are part of the Java Collections Framework and were introduced in Java 8. They provide a powerful way to process data in a functional style, making it easier to work with collections and perform tasks like filtering, mapping, and reducing. Streams enable concise, declarative, and readable code.

Here’s a breakdown of key concepts and features of Java Streams:


1. What is a Stream?

A Stream is a sequence of elements supporting various operations to process data. It doesn’t store data itself but operates on the source (e.g., a collection, array, or I/O channel).

Key Characteristics:

  • Lazy Evaluation: Operations are executed only when a terminal operation is invoked.
  • Pipeline Processing: Operations can be chained (filter, map, etc.), forming a pipeline.
  • Immutable: The original source is not modified.

2. How to Create a Stream

Streams can be created in several ways:

import java.util.*;
import java.util.stream.*;

public class StreamExamples {
    public static void main(String[] args) {
        // From a collection
        List<String> list = Arrays.asList("A", "B", "C");
        Stream<String> stream1 = list.stream();

        // From an array
        String[] array = {"X", "Y", "Z"};
        Stream<String> stream2 = Arrays.stream(array);

        // From a range of numbers
        IntStream stream3 = IntStream.range(1, 5); // 1, 2, 3, 4

        // From static methods
        Stream<String> stream4 = Stream.of("Hello", "World");

        // Infinite streams
        Stream<Integer> stream5 = Stream.iterate(0, n -> n + 2); // 0, 2, 4, 6, ...
        Stream<Double> stream6 = Stream.generate(Math::random);
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Types of Stream Operations

Intermediate Operations

  • These return a new stream and are lazy (not executed until a terminal operation is invoked).
  • Common Intermediate Operations:
    • filter(Predicate): Filters elements based on a condition.
    • map(Function): Transforms each element.
    • sorted() / sorted(Comparator): Sorts elements.
    • distinct(): Removes duplicates.
    • limit(n) / skip(n): Limits/skips elements.

Example:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0) // Keep even numbers
    .map(n -> n * n)         // Square each number
    .collect(Collectors.toList()); // Collect to a list
System.out.println(evenNumbers); // Output: [4, 16]
Enter fullscreen mode Exit fullscreen mode

Terminal Operations

  • These produce a result or a side effect.
  • Common Terminal Operations:
    • collect(Collector): Collects elements into a collection.
    • forEach(Consumer): Performs an action for each element.
    • reduce(BinaryOperator): Reduces the stream to a single value.
    • count(): Counts elements.
    • anyMatch(), allMatch(), noneMatch(): Match predicates.
    • findFirst() / findAny(): Finds an element.

Example:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String result = names.stream()
    .filter(name -> name.startsWith("B")) // Keep names starting with "B"
    .findFirst()
    .orElse("No match");
System.out.println(result); // Output: Bob
Enter fullscreen mode Exit fullscreen mode

4. Stream Collectors

The Collectors utility class provides ways to collect data from streams.

Examples:

  • To List: collect(Collectors.toList())
  • To Map: collect(Collectors.toMap(keyMapper, valueMapper))
  • Joining Strings: collect(Collectors.joining(", "))
  • Summarizing Statistics: collect(Collectors.summarizingInt())

Example:

List<String> names = Arrays.asList("John", "Jane", "Jack");
String result = names.stream()
    .collect(Collectors.joining(", ")); // "John, Jane, Jack"
System.out.println(result);
Enter fullscreen mode Exit fullscreen mode

5. Parallel Streams

Parallel Streams divide tasks into smaller chunks and process them in parallel using multiple threads.

Example:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
    .mapToInt(n -> n * n)
    .sum();
System.out.println(sum); // Output: 55
Enter fullscreen mode Exit fullscreen mode

6. Advanced Features

  • FlatMap: Flattens nested streams (e.g., converting a list of lists into a single stream).
  • Primitive Streams: Specialized streams for int, long, and double (e.g., IntStream, DoubleStream).
  • Custom Collectors: Create custom reduction operations.

Example of flatMap:

List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("A", "B"),
    Arrays.asList("C", "D")
);
List<String> flatList = nestedList.stream()
    .flatMap(List::stream) // Flatten nested lists
    .collect(Collectors.toList());
System.out.println(flatList); // Output: [A, B, C, D]
Enter fullscreen mode Exit fullscreen mode

7. Best Practices

  • Use parallel streams only when operations are computationally intensive and thread-safe.
  • Avoid modifying the original collection in stream pipelines.
  • Use method references (e.g., Class::method) for concise code.

Top comments (0)