Java 8 Stream: groupingBy with multiple Collectors(Java 8 Stream:groupingBy 与多个收集器)
问题描述
我想使用 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<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
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 => ....
这对像Collectors.counting()"这样的一个函数非常有效,但我喜欢链接多个(理想情况下从列表中无限).
This works perfectly fine with one function like "Collectors.counting()" but I like to chain more than one (ideally infinite from a List).
List<Collector<Person, ?, ?>>
有可能做这样的事情吗?
Is it possible to do something like this?
推荐答案
求和求平均的具体问题,使用collectingAndThen
以及 <代码>summarizingDouble:
For the concrete problem of summing and averaging, use collectingAndThen
along with summarizingDouble
:
Map<Integer, Data> result = persons.stream().collect(
groupingBy(Person::getGroup,
collectingAndThen(summarizingDouble(Person::getAge),
dss -> new Data((long)dss.getAverage(), (long)dss.getSum()))));
对于更一般的问题(收集有关您的 Persons 的各种信息),您可以像这样创建一个复杂的收集器:
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<Collector<Person, ?, ?>> collectors = Arrays.asList(
Collectors.averagingInt(Person::getAge),
Collectors.summingInt(Person::getAge));
@SuppressWarnings("unchecked")
Collector<Person, List<Object>, List<Object>> complexCollector = Collector.of(
() -> collectors.stream().map(Collector::supplier)
.map(Supplier::get).collect(toList()),
(list, e) -> IntStream.range(0, collectors.size()).forEach(
i -> ((BiConsumer<Object, Person>) collectors.get(i).accumulator()).accept(list.get(i), e)),
(l1, l2) -> {
IntStream.range(0, collectors.size()).forEach(
i -> l1.set(i, ((BinaryOperator<Object>) 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<Object, Object>)collectors.get(i).finisher()).apply(list.get(i))));
return list;
});
Map<Integer, List<Object>> result = persons.stream().collect(
groupingBy(Person::getGroup, complexCollector));
映射值是列表,其中第一个元素是应用第一个收集器的结果,依此类推.您可以使用 Collectors.collectingAndThen(complexCollector, list -> ...)
添加自定义整理器步骤,以将此列表转换为更合适的内容.
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.
这篇关于Java 8 Stream:groupingBy 与多个收集器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:Java 8 Stream:groupingBy 与多个收集器


- value & 是什么意思?0xff 在 Java 中做什么? 2022-01-01
- 从 finally 块返回时 Java 的奇怪行为 2022-01-01
- C++ 和 Java 进程之间的共享内存 2022-01-01
- 如何使用WebFilter实现授权头检查 2022-01-01
- Eclipse 插件更新错误日志在哪里? 2022-01-01
- Java包名称中单词分隔符的约定是什么? 2022-01-01
- Spring Boot连接到使用仲裁器运行的MongoDB副本集 2022-01-01
- Safepoint+stats 日志,输出 JDK12 中没有 vmop 操作 2022-01-01
- 将log4j 1.2配置转换为log4j 2配置 2022-01-01
- Jersey REST 客户端:发布多部分数据 2022-01-01