Java 24: Introducing the gather Method in java.util.stream

Java 24 introduces a new method, gather(Gatherer), in the java.util.stream API, bringing powerful new capabilities to intermediate operations in streams.

What is a Gatherer?

A Gatherer is an intermediate stream operation that processes elements while considering their relationships with previous elements. Unlike map(), filter(), and flatMap(), which operate on single elements independently, Gatherer enables more complex transformations, such as folding functions, windowing, and concurrent processing.

Gatherer is an intermediate operation, whereas Collectors.collect() is a terminal operation.

Key Features of Gatherer

  • New gather(Gatherer) method added to java.util.Stream in Java 24.
  • Allows processing of elements with context from previous elements.
  • Supports various built-in functions like windowed transformations and concurrent mapping.

Structure of a Gatherer

A Gatherer consists of three parts:

  1. T – Type of input elements.
  2. A – Mutable state type (typically hidden as an implementation detail).
  3. R – Type of output elements.

Core Methods in Gatherer

Internally, a Gatherer operates using the following methods:

  • initializer() (Optional) – Initializes the Gatherer.
  • integrator() (Mandatory) – Integrates elements into the Gatherer.
  • combiner() (Optional) – Merges two Gatherers.
  • finisher() (Optional) – Defines the final processing step.
  • andThen() (Optional) – Chains another Gatherer.

Built-in Gatherers

windowFixed(int windowSize)

Groups elements into fixed-size windows.

List<List<Integer>> windows = Stream.of(1,2,3,4,5,6,7,8)
    .gather(Gatherers.windowFixed(3))
    .toList();
// Output: [[1, 2, 3], [4, 5, 6], [7, 8]]

windowSliding(int windowSize)

Creates overlapping sliding windows.

List<List<Integer>> windowBy2 = Stream.of(1,2,3,4,5,6,7,8)
    .gather(Gatherers.windowSliding(2))
    .toList();
// Output: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8]]

fold(Supplier<R> initial, BiFunction<R, T, R> folder)

Reduces a stream to a single element.

Optional<String> numberString = Stream.of(1,2,3,4,5,6,7,8,9)
    .gather(Gatherers.fold(() -> "", (string, number) -> string + number))
    .findFirst();
// Output: "123456789"

scan(Supplier<R> initial, BiFunction<R, T, R> scanner)

Performs a prefix scan on elements.

List<String> numberStrings = Stream.of(1,2,3,4,5,6,7,8,9)
    .gather(Gatherers.scan(() -> "", (string, number) -> string + number))
    .toList();
// Output: ["1", "12", "123", "1234", "12345", "123456", "1234567", "12345678", "123456789"]

mapConcurrent(int maxConcurrency, Function<T, R> mapper)

Executes a function in parallel using virtual threads.

With the introduction of gather(), Java Streams gain new flexibility in processing data efficiently and expressively. Stay tuned for more insights into Java 24 features!

Leave a comment