How to use a custom Collector in a groupingBy operation

Tim Biegeleisen :

The Oracle trails on reduction with streams gives an example of how to convert a collection of people into a map containing the average age based on gender. It uses the following Person class and code:

public class Person {
    private int age;

    public enum Sex {
        MALE,
        FEMALE
    }

    private Sex sex;

    public Person (int age, Sex sex) {
        this.age = age;
        this.sex = sex;
    }

    public int getAge() { return this.age; }

    public Sex getSex() { return this.sex; }
}

Map<Person.Sex, Double> averageAgeByGender = roster
    .stream()
    .collect(
        Collectors.groupingBy(
            Person::getSex,                      
            Collectors.averagingInt(Person::getAge)));

The above stream code works great, but I wanted to see how to do the same operation while using a custom implementation of a collector. I could find no complete example of how to do this either on Stack Overflow or the net. As to why we might want to do this, as an example, perhaps we would want to compute some kind of weighted average involving the age. In this case, the default behavior of Collectors.averagingInt would not suffice.

Didier L :

Just use Collector.of(Supplier, BiConsumer, BinaryOperator, [Function,] Characteristics...) for those cases:

Collector.of(() -> new double[2],
        (a, t) -> { a[0] += t.getAge(); a[1]++; },
        (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },
        a -> (a[1] == 0) ? 0.0 : a[0] / a[1])
)

Although it might be more readable to define a PersonAverager:

class PersonAverager {
    double sum = 0;
    int count = 0;

    void accept(Person p) {
        sum += p.getAge();
        count++;
    }

    PersonAverager combine(PersonAverager other) {
        sum += other.sum;
        count += other.count;
        return this;
    }

    double average() {
        return count == 0 ? 0 : sum / count;
    }
}

and use it as:

Collector.of(PersonAverager::new,
        PersonAverager::accept,
        PersonAverager::combine,
        PersonAverager::average)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=74106&siteId=1