Search code examples
javajava-8java-stream

Collect successive pairs from a stream


Given an object or primitive stream such as { 0, 1, 2, 3, 4 }, how can I most elegantly transform it into given form (assuming, of course, I've defined class Pair)?

{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }


Solution

  • The Gatherers.windowSliding gatherer in the upcoming Java 24 release (available as a preview language feature since Java 22) supports this:

    Stream.of(0, 1, 2, 3, 4)
            .gather(Gatherers.windowSliding(2))
            .map(list -> new Pair(list.get(0), list.get(1)))
            .toList();
    

    This uses the new Stream.gather method with the new windowSliding gatherer to convert the initial Stream<Integer> ([0, 1, 2, 3, 4]) to a pairwise Stream<List<Integer>> ([[0, 1], [1, 2], [2, 3], [3, 4]]). Each of these lists is then transformed to a Pair using the existing Stream.map method.

    Javadocs

    Gatherer:

    An intermediate operation that transforms a stream of input elements into a stream of output elements, optionally applying a final action when the end of the upstream is reached. […]

    […]

    There are many examples of gathering operations, including but not limited to: grouping elements into batches (windowing functions); de-duplicating consecutively similar elements; incremental accumulation functions (prefix scan); incremental reordering functions, etc. The class Gatherers provides implementations of common gathering operations.

    Stream.gather:

    Returns a stream consisting of the results of applying the given gatherer to the elements of this stream.

    Gatherers.windowSliding

    Returns a Gatherer that gathers elements into windows -- encounter-ordered groups of elements -- of a given size, where each subsequent window includes all elements of the previous window except for the least recent, and adds the next element in the stream. […]

    Example:

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