Этот ответ, который был проверен, основан на нескольких источниках.Исходный код Collectors#averagingInt
был полезен при выяснении синтаксиса лямбда-выражения, используемого ниже.Используется поставщик Double[]
массив второго размера.Первый индекс используется для хранения совокупного возраста человека, а второй - для счетчиков.
public class PersonCollector<T extends Person> implements Collector<T, double[], Double> {
private ToIntFunction<Person> mapper;
public PersonCollector(ToIntFunction<Person> mapper) {
this.mapper = mapper;
}
@Override
public Supplier<double[]> supplier() {
return () -> new double[2];
}
@Override
public BiConsumer<double[], T> accumulator() {
return (a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; };
}
@Override
public BinaryOperator<double[]> combiner() {
return (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; };
}
@Override
public Function<double[], Double> finisher() {
return a -> (a[1] == 0) ? 0.0 : a[0] / a[1];
}
@Override
public Set<Characteristics> characteristics() {
// do NOT return IDENTITY_FINISH here, which would bypass
// the custom finisher() above
return Collections.emptySet();
}
}
List<Person> list = new ArrayList<>();
list.add(new Person(34, Person.Sex.MALE));
list.add(new Person(23, Person.Sex.MALE));
list.add(new Person(68, Person.Sex.MALE));
list.add(new Person(14, Person.Sex.FEMALE));
list.add(new Person(58, Person.Sex.FEMALE));
list.add(new Person(27, Person.Sex.FEMALE));
final Collector<Person, double[], Double> pc = new PersonCollector<>(Person::getAge);
Map<Person.Sex, Double> averageAgeBySex = list
.stream()
.collect(Collectors.groupingBy(Person::getSex, pc));
System.out.println("Male average: " + averageAgeBySex.get(Person.Sex.MALE));
System.out.println("Female average: " + averageAgeBySex.get(Person.Sex.FEMALE));
Это выводит:
Male average: 41.666666666666664
Female average: 33.0
Обратите внимание, что мы передаем ссылку на метод Person::getAge
пользовательскому коллектору, который отображает каждый Person
в коллекции на целочисленное значение возраста.Кроме того, мы не возвращаем Characteristics.IDENTITY_FINISH
из метода characateristics()
.Это будет означать, что наш пользовательский finisher()
будет обойден.