我有两个类Bill和Charge,如下所示。
class Bill {
private String id;
private List<Charge> charges;
// Getters Setters Constructors etc..
}class Charge{
private String typeId;
private double a;
private double b;
// Getters Setters Constructors etc..
}List<Bill> bills = new ArrayList<>();
Bill b1 = new Bill();
b1.setId("1");
List<Charge> charges = new ArrayList<>();
charges.add(new Charge("type-1",20,30));
charges.add(new Charge("type-2",30,30));
b1.setCharges(charges);
Bill b2 = new Bill();
b2.setId("2");
List<Charge> charges2 = new ArrayList<>();
charges2.add(new Charge("type-1",30,40));
charges2.add(new Charge("type-2",40,40));
b2.setCharges(charges2);现在我有了一个方法,这个方法应该根据typeId平均费用,并且每个typeId只返回一个费用
public Bill average(List<Bill> bills){
...
}我希望这个方法返回一个如下所示的账单
Bill{
id:null,
charges:[
{
typeId:"type-1",
a:25,
b:35
},
{
typeId:"type-2",
a:35,
b:35
}
]
}这可以使用for或forEach循环来实现,但我希望解决这个Streams api
发布于 2020-05-20 04:13:22
public static Bill average(List<Bill> bills) {
final List<Charge> charges = bills.stream()
.flatMap(x -> x.getCharges().stream())
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
Charge::getTypeId,
billInfoToAverage()
),
x -> new ArrayList<>(x.values())
));
return new Bill(null, charges);
}
public static Collector<Charge, BillInfoAccumulator, Charge> billInfoToAverage() {
return Collector.of(
BillInfoAccumulator::new,
BillInfoAccumulator::add,
BillInfoAccumulator::combine,
BillInfoAccumulator::average
);
}
class BillInfoAccumulator {
private String typeId;
private final DoubleSummaryStatistics aStats = new DoubleSummaryStatistics();
private final DoubleSummaryStatistics bStats = new DoubleSummaryStatistics();
public void add(Charge charge) {
typeId = charge.getTypeId();
aStats.accept(charge.getA());
bStats.accept(charge.getB());
}
public BillInfoAccumulator combine(BillInfoAccumulator accumulator) {
aStats.combine(accumulator.aStats);
bStats.combine(accumulator.bStats);
return this;
}
public Charge average() {
return new Charge(typeId, aStats.getAverage(), bStats.getAverage());
}
}发布于 2020-05-20 02:02:48
这应该是可行的,尽管它看起来不是很漂亮:
public Bill average(List<Bill> bills) {
List<Charge> avgCharges = bills.stream().flatMap(b -> b.getCharges().stream())
.collect(Collectors.groupingBy(Charge::getTypeId))
.entrySet().stream()
.collect(Collectors.toMap(x -> {
double avgA = x.getValue().stream().mapToDouble(Charge::getA).average().getAsDouble();
double avgB = x.getValue().stream().mapToDouble(Charge::getB).average().getAsDouble();
return new Charge(x.getKey(), avgA, avgB);
}, Map.Entry::getValue))
.keySet().stream().collect(Collectors.toList());
return new Bill("avgCharges", avgCharges);
}测试代码片段:
Bill avg = average(Arrays.asList(b1, b2));
avg.getCharges().stream()
.forEach(c -> System.out.println(c.getTypeId() + "-> a=" + c.getA() + ", b=" + c.getB()));提供以下输出:
type-1-> a=25.0, b=35.0
type-2-> a=35.0, b=35.0发布于 2020-05-20 02:36:52
public Bill average(List<Bill> bills) {
final List<Charge> charges = bills.stream()
.flatMap(x -> x.getCharges().stream())
.collect(Collectors.groupingBy(Charge::getTypeId))
.entrySet().stream()
.map(x -> new Charge(
x.getKey(),
x.getValue().stream().mapToDouble(Charge::getA).average().getAsDouble(),
x.getValue().stream().mapToDouble(Charge::getB).average().getAsDouble()))
.collect(Collectors.toList());
return new Bill(null, charges);
}或
public Bill average(List<Bill> bills) {
return bills.stream()
.flatMap(x -> x.getCharges().stream())
.collect(Collectors.collectingAndThen(Collectors.groupingBy(Charge::getTypeId),
x -> {
final List<Charge> charges = x.entrySet().stream()
.map(y -> new Charge(
y.getKey(),
y.getValue().stream().mapToDouble(Charge::getA).average().getAsDouble(),
y.getValue().stream().mapToDouble(Charge::getB).average().getAsDouble()))
.collect(Collectors.toList());
return new Bill(null, charges);
}));
}https://stackoverflow.com/questions/61894307
复制相似问题