Starting from Java 12, there is Collectors.teeing
which, according to the docs:
Returns a Collector
that is a composite of two downstream collectors. Every element passed to the resulting collector is processed by both downstream collectors, then their results are merged using the specified merge function into the final result.
So, in your case, you could use it this wway:
Result result = statistics.collect(
Collectors.teeing(
Collectors.summarizingInt(Statistic::getSpread),
Collectors.toList(),
Result::new));
Where Result
is as follows:
public class Result {
private final IntSummaryStatistics stats;
private final List<Statistic> list;
Result(IntSummaryStatistics stats, List<Statistic> list) {
this.stats = stats;
this.list = list;
}
// getters
}
Note that if you are on Java 14+, you can use a record for the result, instead of a class:
public record Result(IntSummaryStatistics stats, List<Statistic> list) { }
EDIT: If you are not on Java 12+ yet, you might use this code to create a utility teeing
method:
public static <T, R1, R2, R> Collector<T, ?, R> teeing(
Collector<? super T, ?, R1> downstream1,
Collector<? super T, ?, R2> downstream2,
BiFunction<? super R1, ? super R2, R> merger) {
return teeing0(downstream1, downstream2, merger);
}
private static <T, A1, A2, R1, R2, R> Collector<T, ?, R> teeing0(
Collector<? super T, A1, R1> downstream1,
Collector<? super T, A2, R2> downstream2,
BiFunction<? super R1, ? super R2, R> merger) {
Objects.requireNonNull(downstream1, "downstream1");
Objects.requireNonNull(downstream2, "downstream2");
Objects.requireNonNull(merger, "merger");
Supplier<A1> c1Supplier = Objects.requireNonNull(
downstream1.supplier(), "downstream1 supplier");
Supplier<A2> c2Supplier = Objects.requireNonNull(
downstream2.supplier(), "downstream2 supplier");
BiConsumer<A1, ? super T> c1Accumulator = Objects.requireNonNull(
downstream1.accumulator(), "downstream1 accumulator");
BiConsumer<A2, ? super T> c2Accumulator = Objects.requireNonNull(
downstream2.accumulator(), "downstream2 accumulator");
BinaryOperator<A1> c1Combiner = Objects.requireNonNull(
downstream1.combiner(), "downstream1 combiner");
BinaryOperator<A2> c2Combiner = Objects.requireNonNull(
downstream2.combiner(), "downstream2 combiner");
Function<A1, R1> c1Finisher = Objects.requireNonNull(
downstream1.finisher(), "downstream1 finisher");
Function<A2, R2> c2Finisher = Objects.requireNonNull(
downstream2.finisher(), "downstream2 finisher");
Set<Collector.Characteristics> characteristics;
// Characteristics left as an exercise to the reader :)
class PairBox {
A1 left = c1Supplier.get();
A2 right = c2Supplier.get();
void add(T t) {
c1Accumulator.accept(left, t);
c2Accumulator.accept(right, t);
}
PairBox combine(PairBox other) {
left = c1Combiner.apply(left, other.left);
right = c2Combiner.apply(right, other.right);
return this;
}
R get() {
R1 r1 = c1Finisher.apply(left);
R2 r2 = c2Finisher.apply(right);
return merger.apply(r1, r2);
}
}
return new CollectorImpl<>(PairBox::new, PairBox::add,
PairBox::combine, PairBox::get, characteristics);
}