I am trying to implement a simple collector, which takes a list of collectors and simultaneously collects values in slightly different ways from a stream.
It is quite similar to Collectors.teeing
, but differs in that it
The type signature I want to have is
public static <T, R> Collector<T, ?, List<R>> list(
final List<Collector<T, ?, R>> downstreamCollectors);
One way to create such a collector would be to recursively pair up teeing collectors, like so:
public static <T, R> Collector<T, ?, List<R>> list(
final List<Collector<T, ?, R>> downstreamCollectors) {
return listrec(
Collectors.collectingAndThen(downstreamCollectors.get(0), List::of),
downstreamCollectors.stream().skip(1).toList());
}
private static <T, R> Collector<T, ?, List<R>> listrec(
final Collector<T, ?, List<R>> teedCollectors,
final List<Collector<T, ?, R>> downstreamCollectors) {
if (downstreamCollectors.size() == 0) {
return teedCollectors;
} else {
return listrec(
teeing(
teedCollectors,
downstreamCollectors.get(0),
(l, s) -> Stream.concat(l.stream(), Stream.of(s)).toList()),
downstreamCollectors.stream().skip(1).toList());
}
}
Something feels a little "off" with this solution, so I am trying to create the collector myself, something like:
public static <T, R> Collector<T, ?, List<R>> list2(
final List<Collector<T, ?, R>> downstreamCollectors) {
return Collector.of(
() -> downstreamCollectors.stream().map(c -> c.supplier().get()).toList(),
(accumulators, t) ->
IntStream.range(0, downstreamCollectors.size())
.forEach(
i -> downstreamCollectors.get(i).accumulator().accept(accumulators.get(i), t)),
(accumulator1, accumulator2) ->
IntStream.range(0, downstreamCollectors.size())
.mapToObj(
i ->
downstreamCollectors
.get(i)
.combiner()
.apply(accumulator1.get(i), accumulator2.get(i)))
.toList(),
accumulators ->
IntStream.range(0, downstreamCollectors.size())
.mapToObj(i -> downstreamCollectors.get(i).finisher().apply(accumulators.get(i)))
.toList());
}
Because of the unbounded wildcard in the downstream collectors' accumulator type, this doesn't compile. Changing the type signature to
public static <T, A, R> Collector<? super T, ?, List<R>> list2(
final List<Collector<? super T, A, R>> downstreamCollectors);
solves the problem, but unfortunately renders the method much less usable as the downstream collectors (like the built in collectors from java.util.stream.Collectors
) typically would have a unbounded wildcard in the accumulator type.
Is there another way to implement this, keeping the wildcard in the method signature?
I am using OpenJDK 17.0.2.