Stream API in Java explained

Srikanth
Javarevisited
Published in
3 min readSep 11, 2023

The Stream API in Java is a powerful and expressive feature introduced in Java 8 to work with sequences of data in a functional and declarative style. It allows you to process collections of data (e.g., lists, arrays, or other data sources) with concise and readable code, enabling operations like filtering, mapping, reducing, and more. Streams are part of the java.util.stream package.

Here’s an overview of how the Stream API works and its key components:

Creating Streams:

  • You can create a stream from various data sources, such as collections, arrays, or by using Stream factories like Stream.of() or Stream.generate(). For example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();

Intermediate Operations:

  • Intermediate operations are operations that transform an existing stream into a new one. These operations are lazy, meaning they don’t perform any processing until a terminal operation is invoked. Common intermediate operations include:
  • filter(Predicate<T> predicate): Filters elements based on a given condition.
  • map(Function<T, R> mapper): Transforms elements using the provided function.
  • flatMap(Function<T, Stream<R>> mapper): Flattens nested streams into a single stream.
  • distinct(): Removes duplicate elements.
  • sorted(): Sorts elements.
  • limit(long maxSize): Truncates the stream to a specified size.
  • skip(long n): Skips the first n elements.
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.collect(Collectors.toList());

Terminal Operations:

  • Terminal operations trigger the processing of the stream and produce a result or a side effect. Some common terminal operations include:
  • collect(Collector<T, A, R> collector): Collects the stream into a container (e.g., a List or Map).
  • forEach(Consumer<T> action): Performs an action for each element in the stream.
  • reduce(BinaryOperator<T> accumulator): Reduces the stream to a single value.
  • count(): Returns the count of elements in the stream.
  • min(Comparator<T> comparator) and max(Comparator<T> comparator): Finds the minimum and maximum values in the stream.
  • anyMatch(Predicate<T> predicate), allMatch(Predicate<T> predicate), and noneMatch(Predicate<T> predicate): Check if any, all, or none of the elements match a given condition.
long count = numbers.stream().filter(n -> n > 2).count();

Short-Circuiting Operations:

  • Some terminal operations, like findFirst(), findAny(), anyMatch(), and allMatch(), can short-circuit and return results as soon as they find a match, without processing the entire stream.
Optional<Integer> firstEven = numbers.stream().filter(n -> n % 2 == 0).findFirst();

Parallel Streams:

  • You can easily parallelize stream operations using parallelStream() to take advantage of multi-core processors for improved performance.
List<Integer> parallelEvenNumbers = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());

The Stream API in Java simplifies data manipulation and processing, making your code more concise and readable while promoting a functional programming style. It’s a powerful tool for working with collections in a more expressive way.

Clap 👏 if you like the content and Follow me 💌 if you want to read more such articles. And you can buy me a coffee at BuyMeACoffee

Thanks for reading.

--

--

Srikanth
Javarevisited

Passionate writer in Programming, Backend Development