我'm new to using Java 8 Stream APIs but I' m希望用它来解决以下问题 . 假设我有一个名为 InputRecord
的POJO,其中包含 name
, fieldA
和 fieldB
属性,可以表示以下各行记录:
name | fieldA | fieldB
----------------------
A | 100 | 1.1
A | 150 | 2.0
B | 200 | 1.5
A | 120 | 1.3
InputRecord
看起来像:
public class InputRecord {
private String name;
private Integer fieldA;
private BigDecimal fieldB;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getFieldA() {
return fieldA;
}
public void setFieldA(Integer fieldA) {
this.fieldA = fieldA;
}
public BigDecimal getFieldB() {
return fieldB;
}
public void setFieldB(BigDecimal fieldB) {
this.fieldB = fieldB;
}
}
上述四条记录需要合并为两个按名称分组的记录,其中:
-
属性
fieldA
相加 -
属性
fieldB
总和 -
组合记录包括
fieldC
属性,该属性是将fieldA
和fieldB
的累加和相乘的结果 .
因此上面的结果将是:
name | sumOfFieldA | sumOfFieldB | fieldC (sumOfFieldA*sumOfFieldB)
-------------------------------------------------------------------
A | 370 | 4.4 | 1628
B | 200 | 1.5 | 300
名为 OutputRecord
的另一个POJO将表示组合记录的每个行记录:
public class OutputRecord {
private String name;
private Integer sumOfFieldA;
private BigDecimal sumOfFieldB;
private BigDecimal fieldC;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSumOfFieldA() {
return sumOfFieldA;
}
public void setSumOfFieldA(Integer sumOfFieldA) {
this.sumOfFieldA = sumOfFieldA;
}
public BigDecimal getSumOfFieldB() {
return sumOfFieldB;
}
public void setSumOfFieldB(BigDecimal sumOfFieldB) {
this.sumOfFieldB = sumOfFieldB;
}
public BigDecimal getFieldC() {
return fieldC;
}
public void setFieldC(BigDecimal fieldC) {
this.fieldC = fieldC;
}
}
将InputRecords列表转换为OutputRecords列表有哪些好的方法/解决方案?
我看到以下链接是否会有所帮助,但我一直试图将收藏家放在 fieldA
和 fieldB
以便为 fieldC
形成一个新的收藏家:Java 8 Stream: groupingBy with multiple Collectors
Collector<InputRecord, ?, Integer> fieldACollector = Collectors.summingInt(InputRecord::getFieldA);
Collector<InputRecord, ?, BigDecimal> fieldBCollector = Collectors.reducing(BigDecimal.ZERO, InputRecord::getFieldB, BigDecimal::add);
List<Collector<InputRecord, ?, ?>> collectors = Arrays.asList(fieldACollector, fieldBCollector); // need a fieldCCollector object in the list
然后将使用 collectors
对象创建 complexCollector
对象(根据上面链接中Tagir Valeev接受的答案) .
4 回答
对我来说,最干净的方法是为此构建一个自定义收集器 . 这里有多行代码,但您可以在方法下隐藏它,因此您的最终操作将如下所示:
而实际
toOutputRecords
将是:您可以使用Stream.reduce(..)将两个记录转换为单个记录 . 它创建了一堆需要由JVM进行垃圾回收的临时对象 .
您可以使用组合和聚合函数从InputRecords列表生成OutputRecords列表 .
我认为组合多个收集器的通用实用方法将更好,而不是定义定制的收集器(我认为它很复杂且难以维护) . 例如:
使用combine方法,解决方案将是:
以下是AbacusUtil中
combine
的示例实现