Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split stream into substreams with N elements

Can we somehow split stream into substreams with no more than N elements in Java? For example

Stream<Integer> s = Stream.of(1,2,3,4,5);
Stream<Stream<Integer>> separated = split(s, 2);
// after that separated should contain stream(1,2), stream(3,4), stream(5)

splitting by two streams solution is correct only for 2 streams, the same for N streams will be very ugly and write-only.

like image 253
maxpovver Avatar asked Sep 20 '25 06:09

maxpovver


2 Answers

You can't split a Stream into 2 or more Streass easily and directly. The only way the procedural one consisting of collecting the elements to the List by the couples and mapping them back to Stream again:

Stream<Integer> s = Stream.of(1,2,3,4,5);
List<Integer> list = s.collect(Collectors.toList());
int size = list.size();
    
List<List<Integer>> temp = new ArrayList<>();
List<Integer> temp2 = new ArrayList<>();
    
int index = 0;
for (int i=0; i<size; i++) {
    temp2.add(list.get(i));
    if (i%2!=0) {
        temp.add(temp2);
        temp2 = new ArrayList<>();
    }
    if (i == size - 1) {
        temp.add(temp2);
    }
}
Stream<Stream<Integer>> stream = temp.stream().map(i -> i.stream());

As you see it's a really long way an not worth. Wouldn't be better to store the pairs in the List rather than Stream? The java-stream API is not used for data storage but their processing.

like image 197
Nikolas Charalambidis Avatar answered Sep 21 '25 19:09

Nikolas Charalambidis


The JEP 485: Stream Gatherers Java 24 language feature (available as a preview language feature since Java 22) adds built-in support for partitioning a stream into lists of a given size. These lists could then be mapped into streams.

// A stream containing 2 (or less) element streams: [[1, 2], [3, 4], [5]]
Stream<Stream<Integer>> separated = Stream.of(1, 2, 3, 4, 5)
        .gather(Gatherers.windowFixed(2))
        .map(Collection::stream);

This uses the new Stream.gather method with the new built-in Gatherers.windowFixed gatherer to convert the initial Stream<T> to a Stream<List<T>>. This is then converted to a Stream<Stream<T>> using Stream.map with Collection::stream as the mapping function.

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.windowFixed

Returns a Gatherer that gathers elements into windows -- encounter-ordered groups of elements -- of a fixed size. If the stream is empty then no window will be produced. The last window may contain fewer elements than the supplied window size.

Example:

// will contain: [[1, 2, 3], [4, 5, 6], [7, 8]]
List<List<Integer>> windows =
    Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowFixed(3)).toList();
like image 24
M. Justin Avatar answered Sep 21 '25 18:09

M. Justin