我正在尝试理解java 8流。我有两节课。
public class ObjectA {
private String fieldA;
private String fieldB;
private String fieldC;
private double valueA;
private double valueB;
private double valueC;
}
public class ObjectB {
private String fieldA;
private double valueA;
private double valueB;
private double valueC;
}
我正在尝试将List<ObjectA>
转换为List<ObjectB>
,方法是对fieldA
进行分组并使用流汇总valueA
,valueB
,valueC
,但无法弄清楚如何做到这一点。
这是我想要做的:
Map<String,ObjectB> fieldCObjectBMap = new HashMap<>();
for(ObjectA objectA : objectAList) {
if(fieldCObjectBMap.size != 0 && fieldCObjectBMap.keyset().contains(objectA.getFieldC)) {
ObjectB objectB = fieldCObjectBMap.get(objectA.getFieldC);
objectB.setValueA(objectB.getValueA()+objectA.getValueA());
objectB.setValueB(objectB.getValueB()+objectA.getValueB());
objectB.setValueC(objectB.getValueC()+objectA.getValueC());
fieldCObjectBMap.put(objectA.getFieldC,objectB);
} else {
ObjectB objectB = fieldCObjectBMap.get(objectA.getFieldC);
objectB.setValueA(objectA.getValueA());
objectB.setValueB(objectA.getValueB());
objectB.setValueC(objectA.getValueC());
fieldCObjectBMap.put(objectA.getFieldC,objectB);
}
}
List<ObjectB> objectBList = fieldCObjectBMap.values();
答案 0 :(得分:2)
根据您描述的规则建议将List<ObjectA>
转换为List<ObjectB>
的转换器:
class AtoBCollector {
private static ObjectB makeFromA(ObjectA a) {
return new ObjectB(a.getFieldA(), a.getValueA(), a.getValueB(), a.getValueC());
}
private static BinaryOperator<ObjectB> reduceB = (b1, b2) ->
new ObjectB(b1.getFieldA(),
b1.getValueA() + b2.getValueA(),
b1.getValueB() + b2.getValueB(),
b1.getValueC() + b2.getValueC());
static List<ObjectB> collect(List<ObjectA> objectAList) {
return new ArrayList<>(objectAList.stream()
.map(AtoBCollector::makeFromA)
.collect(toMap(ObjectB::getFieldA, identity(), reduceB))
.values());
}
}
toMap的作用是:第一次遇到fieldA
的值时,它会将Map<String, ObjectB>
作为键,将其作为ObjectB
的相应实例的值。下次它将合并两个ObjectB
个实例与reduceB
。
请注意,values()
会返回Collection
,您必须明确创建List
。在许多情况下,您可以直接使用Collection
,这是首选。
作为上述内容的替代方案,您可以内联makeFromA
和reduceB
并跳过AtoBCollector
的定义。某些人认为结果代码的可读性较差。做出最好的判断。
作为另一种选择,如果ObjectA
和ObjectB
的源代码在您的控制之下,您可以将makeFromA
的功能实现为ObjectB
的构造函数和/或reduceB
作为ObjectB
的方法。
答案 1 :(得分:1)
一种方法是使用Collectors.toMap
作为@Manos Nikolaidis在答案中所做的。另一种方法是使用自定义收集器。我将在静态帮助器方法中执行此操作,使用本地类Acc
来累积部分结果:
static Collector<ObjectA, ?, ObjectB> summingFields() {
class Acc {
ObjectB b = new ObjectB();
void sum(ObjectA a) {
this.b.setFieldA(a.getFieldA());
this.b.setValueA(b.getValueA() + a.getValueA());
this.b.setValueB(b.getValueB() + a.getValueB());
this.b.setValueC(b.getValueC() + a.getValueC());
}
Acc merge(Acc other) {
this.b.setValueA(this.b.getValueA() + other.b.getValueA());
this.b.setValueB(this.b.getValueB() + other.b.getValueB());
this.b.setValueC(this.b.getValueC() + other.b.getValueC());
return this;
}
ObjectB getB() {
return this.b;
}
}
return Collector.of(Acc::new, Acc::sum, Acc::merge, Acc::getB);
}
这使用方法Collector.of
,它根据其参数创建自定义收集器。然后,您可以使用summingFields
方法获取Collection<ObjectB>
,如下所示:
Collection<ObjectB> grouped = objectAList.stream()
.collect(Collectors.groupingBy(
ObjectA::getFieldA,
summingFields()))
.values();
但是,如果您可以在ObjectB
类中添加以下几种方法,那么一切都会轻松得多:
public void sum(ObjectA a) {
this.fieldA = a.getFieldA();
this.valueA += a.getValueA();
this.valueB += a.getValueB();
this.valueC += a.getValueC();
}
public ObjectB merge(ObjectB b) {
this.valueA += b.getValueA();
this.valueB += b.getValueB();
this.valueC += b.getValueC();
return this;
}
然后,您可以使用Collector.of
这些方法:
Collection<ObjectB> grouped = objectAList.stream()
.collect(Collectors.groupingBy(
ObjectA::getFieldA,
Collector.of(ObjectB::new, ObjectB::sum, ObjectB::merge)))
.values();