我想通过一个分类器使用Java 8 Stream和Group,但是有多个Collector函数 . 因此,在分组时,例如计算一个场(或可能是另一个场)的平均值和总和 .
我试着用一个例子来简化这个:
public void test() {
List<Person> 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<Integer, Data> 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
}
结果应该是一个将分组结果关联起来的Map
1 => Data(average(18, 20, 30), sum(18, 20, 30))
2 => Data(average(30, 29), sum(30, 29))
3 => ....
这与“Collectors.counting()”之类的函数完美配合,但我喜欢链接多个(理想情况下,List中的无限) .
List<Collector<Person, ?, ?>>
有可能做这样的事吗?
4 回答
对于求和和求平均的具体问题,请使用collectingAndThen和summarizingDouble:
对于更通用的问题(收集关于你人员的各种事情),你可以创建一个像这样的复杂收集器:
映射值是列表,其中第一个元素是应用第一个收集器的结果,依此类推 . 您可以使用
Collectors.collectingAndThen(complexCollector, list -> ...)
添加自定义修整器步骤,以将此列表转换为更合适的列表 .通过使用 Map 作为输出类型,可以有一个潜在的无限减速器列表,每个减速器都会生成自己的统计数据并将其添加到 Map 中 .
...
产品:
您应该构建一个抽象,它是收集器的聚合器,而不是链接收集器:使用接受收集器列表的类实现
Collector
接口,并将每个方法调用委托给每个收集器 . 然后,最后,返回new Data()
,其中包含嵌套收集器生成的所有结果 .您可以通过使用
Collector.of(supplier, accumulator, combiner, finisher, Collector.Characteristics... characteristics)
来避免使用所有方法声明创建自定义类.finisher
lambda将调用每个嵌套收集器的整理器,然后返回Data
实例 .你可以链接他们,
收集器只能生成一个对象,但此对象可以包含多个值 . 例如,您可以返回一个Map,其中 Map 为您要返回的每个收集器都有一个条目 .
你可以使用
Collectors.of(HashMap::new, accumulator, combiner);
您的
accumulator
将有一个收集器 Map ,其中生成的 Map 的键与收集器的名称相匹配 . 当并行执行时,组合器需要一种方法来组合多个结果esp .通常,内置收集器使用数据类型来获得复杂的结果 .
来自收藏家
并在自己的 class