首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用JAVA streams从对象列表中查找平均值

如何使用JAVA streams从对象列表中查找平均值
EN

Stack Overflow用户
提问于 2020-05-19 23:03:42
回答 4查看 116关注 0票数 0

我有两个类Bill和Charge,如下所示。

代码语言:javascript
复制
class Bill {

    private String id;
    private List<Charge> charges; 
    // Getters Setters Constructors etc..

}
代码语言:javascript
复制
class Charge{

    private String typeId;
    private double a;
    private double b;
    // Getters Setters Constructors etc..
}
代码语言:javascript
复制
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只返回一个费用

代码语言:javascript
复制
public Bill average(List<Bill> bills){
...
}

我希望这个方法返回一个如下所示的账单

代码语言:javascript
复制
Bill{
    id:null,
    charges:[
        {
            typeId:"type-1",
            a:25,
            b:35
        },
        {
            typeId:"type-2",
            a:35,
            b:35
        }
    ]
}

这可以使用for或forEach循环来实现,但我希望解决这个Streams api

EN

回答 4

Stack Overflow用户

发布于 2020-05-20 04:13:22

代码语言:javascript
复制
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());
    }
}
票数 1
EN

Stack Overflow用户

发布于 2020-05-20 02:02:48

这应该是可行的,尽管它看起来不是很漂亮:

代码语言:javascript
复制
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);
}

测试代码片段:

代码语言:javascript
复制
Bill avg = average(Arrays.asList(b1, b2));

avg.getCharges().stream()
.forEach(c -> System.out.println(c.getTypeId() + "-> a=" + c.getA() + ", b=" + c.getB()));

提供以下输出:

代码语言:javascript
复制
type-1-> a=25.0, b=35.0
type-2-> a=35.0, b=35.0
票数 0
EN

Stack Overflow用户

发布于 2020-05-20 02:36:52

代码语言:javascript
复制
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);
}

代码语言:javascript
复制
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);
                    }));
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61894307

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档