Tested the Gatherer implementation, and it is a very nice work.
I have always wanted a way of writing my own stream operations and also have
incomplete composable pipelines which could be combined in different ways. Very
interesting.
The four Gatherer functions - initializer/integrator/combiner/finisher - all
return function objects.
In the javadoc for these Gatherer functions, no requirements are described for
these functions.
(though API note on class level says "Each invocation of initializer(),
integrator(), combiner(), and finisher() must return a semantically identical
result.")
It would be nice to have the requirements stated on the function level also.
Looking at the class level javadoc is not always done.
How often and in which sequence these functions are called are of interest,
especially for stateful Gatherers.
I created a simple gatherer which prints some trace info for these functions
(see below).
It is tested using branch "pr/16420" of the jdk pr. 21.nov.
- The "initializer()" function are in some cases called twice from the
library.
- The "initializer().get()" function is called only once in the tested
scenarios.
This is expected since the method is called "initializer", but this
behaviour could have been documented.
- The sequence these functions are called varies from use case to use case,
sometimes the "integrator()" is called before "initializer()".
Sometimes the "finisher()" are called before first call to "integrator()",
sometimes after.
I've marked the unexpected/unneccesary/repeated calls with ? marks.
All this may be correct, but is it ?
------------------- Trace --------------------
Using andThen Using separate gathers Using
separate gathers with map(identity)
Creates WithIndex(A) Creates WithIndex(A) Creates
WithIndex(A)
Creates WithIndex(B) Creates Integrator(A) Creates
Integrator(A)
Creates WithIndex(C) Creates WithIndex(B) Creates
WithIndex(B)
Creates Initializer(A) Creates Initializer(A) Creates
Integrator(B)
Creates Integrator(A) ?Creates Integrator(A) Creates
WithIndex(C)
Creates Finisher(A) Creates Finisher(A) Creates
Integrator(C)
Creates Initializer(B) Creates Initializer(B) Calls
Collectors.toList
Creates Integrator(B) Creates Integrator(B) Finished
Collectors.toList
Creates Finisher(B) Creates Finisher(B) ?Creates
Integrator(C)
Creates Initializer(C) Creates WithIndex(C) ?Creates
Integrator(B)
Creates Integrator(C) ?Creates Initializer(A) ?Creates
Integrator(A)
Creates Finisher(C) ?Creates Integrator(A) Creates
Initializer(A)
Calls Collectors.toList ?Creates Finisher(A) Invokes
Initializer(A)
Finished Collectors.toList ?Creates Initializer(B) Creates
Initializer(B)
Invokes Initializer(A) ?Creates Integrator(B) Invokes
Initializer(B)
Invokes Initializer(B) ?Creates Finisher(B) Creates
Initializer(C)
Invokes Initializer(C) Creates Initializer(C) Invokes
Initializer(C)
Invokes Integrator(A) Creates Integrator(C) Invokes
Integrator(A)
Invokes Integrator(B) Creates Finisher(C) Invokes
Integrator(B)
Invokes Integrator(C) Calls Collectors.toList Invokes
Integrator(C)
Invokes Integrator(A) Finished Collectors.toList Invokes
Integrator(A)
Invokes Integrator(B) Invokes Initializer(A) Invokes
Integrator(B)
Invokes Integrator(C) Invokes Initializer(B) Invokes
Integrator(C)
Invokes Integrator(A) Invokes Initializer(C) Invokes
Integrator(A)
Invokes Integrator(B) Invokes Integrator(A) Invokes
Integrator(B)
Invokes Integrator(C) Invokes Integrator(B) Invokes
Integrator(C)
Invokes Finisher(A) Invokes Integrator(C) Creates
Finisher(A)
Invokes Finisher(B) Invokes Integrator(A) Invokes
Finisher(A)
Invokes Finisher(C) Invokes Integrator(B) Creates
Finisher(B)
Invokes Integrator(C) Invokes
Finisher(B)
Invokes Integrator(A) Creates
Finisher(C)
Invokes Integrator(B) Invokes
Finisher(C)
Invokes Integrator(C)
Invokes Finisher(A)
Invokes Finisher(B)
Invokes Finisher(C)
----- Java code --------------------
package org.example;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Gatherer;
import java.util.stream.Stream;
import static java.lang.StringTemplate.STR;
import static java.util.function.Function.identity;
public class GatherEx {
static <T> Gatherer<T, AtomicInteger, Indexed<T>> withIndex(String name) {
return new WithIndexGatherer<>(name);
}
static class WithIndexGatherer<T> implements Gatherer<T, AtomicInteger,
Indexed<T>> {
final String name;
public WithIndexGatherer(String name) {
System.out.println(STR. "Creates WithIndex(\{ name })" );
this.name = name;
}
@Override
public Supplier<AtomicInteger> initializer() {
System.out.println(STR. "Creates Initializer(\{ name })" );
return () -> {
System.out.println(STR. "Invokes Initializer(\{ name })" );
return new AtomicInteger(0);
};
}
@Override
public Integrator<AtomicInteger, T, Indexed<T>> integrator() {
System.out.println(STR. "Creates Integrator(\{ name })" );
return (state, element, downstream) ->
{
System.out.println(STR. "Invokes Integrator(\{ name })" );
return downstream.push(new Indexed<>(state.getAndIncrement(),
element));
};
}
@Override
public BiConsumer<AtomicInteger, Downstream<? super Indexed<T>>>
finisher() {
System.out.println(STR. "Creates Finisher(\{ name })" );
return (_, _) -> {
System.out.println(STR. "Invokes Finisher(\{ name })" );
Gatherer.super.finisher();
};
}
}
record Indexed<T>(int i, T data) {
}
public static void main(String[] args) {
System.out.println("Using andThen");
System.out.println(Stream.of(1, 2, 3)
.gather(withIndex("A")
.andThen(withIndex("B"))
.andThen(withIndex("C")))
.collect(createCollector()));
System.out.println("Using separate gathers");
System.out.println(Stream.of(1, 2, 3)
.gather(withIndex("A"))
.gather(withIndex("B"))
.gather(withIndex("C"))
.collect(createCollector()));
System.out.println("Using separate gathers with map(identity)");
System.out.println(Stream.of(1, 2, 3)
.map(identity())
.gather(withIndex("A"))
.map(identity())
.gather(withIndex("B"))
.map(identity())
.gather(withIndex("C"))
.map(identity())
.collect(createCollector()));
}
private static Collector<? super Indexed<?>, ?, List<Indexed<?>>>
createCollector() {
System.out.println("Calls Collectors.toList");
try {
return Collectors.toList();
} finally {
System.out.println("Finished Collectors.toList");
}
}
}