Java 22 released few weeks ago and with that comes the gather() method that accepts Gatherer interface in Stream API as a preview feature.
When Stream API was released with Java 8 there was plenty of joy across java developers, but its limited function hid its true potential. Developers often create a list out of the Stream and perform any additional operation imperatively, which often leads to complicated, error-prone code.
To overcome this limitation, with Java 22, Gatherer interface and gather() method introduced to perform complex transformation as 1:1, 1:n, n:1 and n:m.
Now lets go thorough some of the inbuilt Gatherers and its use cases.
Install JDK 22 and enable preview mode by parsing the flag --enable-preview when executing the program.
Gatherers.windowFixed(int)
Window fixed implementation provides a way to group the stream elements in the order into group of fixed number of elements.
Key points are
1. No window will be provided if the list is empty
2. Last window may have less number of element than the fixed size given.
Suppose if we have following list of number 1, 2, 3, 4, 5, 6, 7, 8 and if want to group them into group of 3 elements and find the max number among them, then it can be done in this way
var numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
var maxNumbers = numbers.stream()
.gather(Gatherers.windowFixed(3))
.peek(System.out::println)
.map(values -> values.stream().max(Comparator.naturalOrder()).orElseThrow())
.toList();
System.out.println(maxNumbers);and the result would be
[1, 2, 3]
[4, 5, 6]
[7, 8]
Result [3, 6, 8]Gatherers.windowSliding(int)
This is similar to window fixed except that it makes the group in sliding one by one, , until the last element is included.
In case if there are numbers 1, 2, 3 then windowSliding(2) would provide groups as [1,2] and [2, 3]
Key points are
1. No window provided for empty list
2. If the list contains less elements than sliding size then single window with all the elements provided
3. If the list contains same amount of element or more, than the sliding size then one or more windows where all of its size same as sliding size are provided.
Suppose if we have following list of number 1, 2, 3, 4, 5 and if want to group them into slide of 3 elements and find the max number among them, then it can be done in this way.
var numbers = List.of(1, 2, 3, 4, 5);
var maxNumbers = numbers.stream()
.gather(Gatherers.windowSliding(3))
.peek(System.out::println)
.map(values -> values.stream().max(Comparator.naturalOrder()).orElseThrow())
.toList();
System.out.println("Result " + maxNumbers);and the result would be
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
Result [3, 4, 5]Gatherers.fold(Supplier, BiFunction)
This is similar to reduce, where a single element is generated from list of element.
Suppose if we have following list of numbers 1, 2, 3, 4, 5 then fold can be used to add all this numbers.
var numbers = List.of(1, 2, 3, 4, 5);
var maxNumbers = numbers.stream()
.gather(Gatherers.fold(() -> 0, Integer::sum))
.toList();
System.out.println("Result " + maxNumbers); and the result would be
Result [15]Gatherers.scan(Supplier, BiFunction)
scan is similar to fold except that scan will push the each iteration and fold will push only in the end of the stream.
Suppose if we have following list of numbers 1, 2, 3, 4, 5 and if we want to accumulate the value of each elements with its previous values and get the result as 1, 3, 6, 10, 15.
var numbers = List.of(1, 2, 3, 4, 5);
var maxNumbers = numbers.stream()
.gather(Gatherers.scan(() -> 0, Integer::sum))
.toList();
System.out.println("Result " + maxNumbers);and the result is
Result [1, 3, 6, 10, 15]More interesting thing about Gatherer is that it brings the possibility of creating our own Gatherer and integrate into Stream.
In the Gatherers.windowFixed example there was a part where max number is found in each window. For that map(values -> values.stream().max(Comparator.naturalOrder()).orElseThrow()) was used and lets try to achieve same thing using our own Gatherer implementation.
public static Gatherer<List<Integer>, ?, Integer> maxNumber() {
class MaxNumber {
boolean integrate(List<Integer> elements, Gatherer.Downstream<? super Integer> downstream) {
var maxValue = elements.stream().max(Comparator.naturalOrder()).orElseThrow();
return downstream.push(maxValue);
}
}
return Gatherer.ofSequential(
MaxNumber::new,
Gatherer.Integrator.<MaxNumber, List<Integer>, Integer>ofGreedy(MaxNumber::integrate)
);
}var numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
var maxNumbers = numbers.stream()
.gather(Gatherers.windowFixed(3))
.gather(maxNumber()) // Our maxNumber gatherer is used
.toList();
System.out.println("Result " + maxNumbers);and the result is
Result [3, 6, 8]To sum up, Gatherer interface introduced with Stream API looks promising as it allows great level of extendibility to the Stream processing and hopefully would allow us to do many more stream magics in future.
No comments:
Post a Comment