Java Streams

Java Streams

Java Streams are part of the java.util.stream package introduced in Java 8. They provide a way to process sequences of data in a functional programming style. Streams can be used to perform operations like filtering, mapping, and reducing on collections of data.

Here are some important details and methods of Java Streams:

Core Characteristics:

  1. Stream Pipeline: A stream works by chaining three components:

    • Source: Data input (e.g., a collection, array, or I/O channel).

    • Intermediate Operations: Transformations (e.g., filter, map).

    • Terminal Operations: Results (e.g., collect, forEach).

  2. Lazy Evaluation: Intermediate operations are evaluated only when a terminal operation is executed.

  3. Immutability: Stream does not modify the original data source.

  4. Single-Use: Once a stream is consumed, it cannot be reused.


Commonly Used Stream Methods

1. Source Methods

  • stream() / parallelStream(): Convert a collection into a sequential or parallel stream.

  • Stream.of(...): Creates a stream from a set of elements.

  • Arrays.stream(array): Converts an array into a stream.

2. Intermediate Operations (Return a Stream)

  • filter(Predicate<T>): Filters elements based on a condition.

      list.stream().filter(x -> x > 5).collect(Collectors.toList());
    
  • map(Function<T, R>): Transforms elements into another type.

      list.stream().map(String::toUpperCase).collect(Collectors.toList());
    
  • flatMap(Function<T, Stream<R>>): Flattens nested structures.

      nestedList.stream().flatMap(List::stream).collect(Collectors.toList());
    
  • distinct(): Removes duplicates.

      list.stream().distinct().collect(Collectors.toList());
    
  • sorted(Comparator<T>): Sorts elements in natural or custom order.

      list.stream().sorted().collect(Collectors.toList());
    
  • limit(long n): Limits the number of elements in the stream.

      list.stream().limit(3).collect(Collectors.toList());
    
  • skip(long n): Skips the first n elements.

      list.stream().skip(2).collect(Collectors.toList());
    

3. Terminal Operations (Produce Results)

  • forEach(Consumer<T>): Performs an action for each element.

      list.stream().forEach(System.out::println);
    
  • collect(Collector<T, A, R>): Converts a stream into a collection or other forms.

      list.stream().collect(Collectors.toList());
    
  • reduce(BinaryOperator<T>): Combines elements into a single result.

      int sum = list.stream().reduce(0, Integer::sum);
    
  • toArray(): Converts the stream into an array.

      Object[] array = list.stream().toArray();
    
  • count(): Counts the number of elements in the stream.

      long count = list.stream().count();
    
  • anyMatch(Predicate<T>) / allMatch(Predicate<T>) / noneMatch(Predicate<T>): Tests elements against a condition.

      boolean hasEven = list.stream().anyMatch(x -> x % 2 == 0);
    
  • findFirst() / findAny(): Retrieves an element from the stream.

      Optional<Integer> first = list.stream().findFirst();
    

4. Short-Circuit Operations

These operations terminate as soon as their goal is achieved:

  • anyMatch, allMatch, noneMatch, findFirst, findAny, limit.

Example Usage

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Jack", "Doe");

        // Filter names starting with 'J' and convert to uppercase
        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("J"))
                                          .map(String::toUpperCase)
                                          .collect(Collectors.toList());

        System.out.println(filteredNames); // Output: [JOHN, JANE, JACK]
    }
}