collector list 多个分组_Java 8 Stream:使用多个收集器进行分组

I want to use a Java 8 Stream and Group by one classifier but have multiple Collector functions. So when grouping, for example the average and the sum of one field (or maybe another field) is calculated.

I try to simplify this a bit with an example:

public void test() {

List persons = new ArrayList<>();

persons.add(new Person("Person One", 1, 18));

persons.add(new Person("Person Two", 1, 20));

persons.add(new Person("Person Three", 1, 30));

persons.add(new Person("Person Four", 2, 30));

persons.add(new Person("Person Five", 2, 29));

persons.add(new Person("Person Six", 3, 18));

Map result = persons.stream().collect(

groupingBy(person -> person.group, multiCollector)

);

}

class Person {

String name;

int group;

int age;

// Contructor, getter and setter

}

class Data {

long average;

long sum;

public Data(long average, long sum) {

this.average = average;

this.sum = sum;

}

// Getter and setter

}

The result should be a Map that associates the result of grouping like

1 => Data(average(18, 20, 30), sum(18, 20, 30))

2 => Data(average(30, 29), sum(30, 29))

3 => ....

This works perfectly fine with one function like "Collectors.counting()" but I like to chain more than one (ideally infinite from a List).

List>

Is it possible to do something like this?

解决方案

For the concrete problem of summing and averaging, use collectingAndThen along with summarizingDouble:

Map result = persons.stream().collect(

groupingBy(Person::getGroup,

collectingAndThen(summarizingDouble(Person::getAge),

dss -> new Data((long)dss.getAverage(), (long)dss.getSum()))));

For the more generic problem (collect various things about your Persons), you can create a complex collector like this:

// Individual collectors are defined here

List> collectors = Arrays.asList(

Collectors.averagingInt(Person::getAge),

Collectors.summingInt(Person::getAge));

@SuppressWarnings("unchecked")

Collector, List> complexCollector = Collector.of(

() -> collectors.stream().map(Collector::supplier)

.map(Supplier::get).collect(toList()),

(list, e) -> IntStream.range(0, collectors.size()).forEach(

i -> ((BiConsumer) collectors.get(i).accumulator()).accept(list.get(i), e)),

(l1, l2) -> {

IntStream.range(0, collectors.size()).forEach(

i -> l1.set(i, ((BinaryOperator) collectors.get(i).combiner()).apply(l1.get(i), l2.get(i))));

return l1;

},

list -> {

IntStream.range(0, collectors.size()).forEach(

i -> list.set(i, ((Function)collectors.get(i).finisher()).apply(list.get(i))));

return list;

});

Map> result = persons.stream().collect(

groupingBy(Person::getGroup, complexCollector));

Map values are lists where first element is the result of applying the first collector and so on. You can add a custom finisher step using Collectors.collectingAndThen(complexCollector, list -> ...) to convert this list to something more appropriate.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值