按两个字段分组,然后对BigDecimal进行求和

15

我有一个税收清单:

TaxLine = title:"New York Tax", rate:0.20, price:20.00
TaxLine = title:"New York Tax", rate:0.20, price:20.00
TaxLine = title:"County Tax", rate:0.10, price:10.00

TaxLine类是

public class TaxLine {
    private BigDecimal price;
    private BigDecimal rate;
    private String title;
}

我希望基于唯一的titlerate将它们合并,然后添加price,期望结果如下:

 TaxLine = title:"New York Tax", rate:0.20, price:40.00
 TaxLine = title:"County Tax", rate:0.10, price:10.00

我该如何在Java 8中实现这个?

在Java 8中按多个字段名称分组,不会计算一个字段的总和,它只能按这两个字段进行分组。

3个回答

24

原理与链接的问题相同,只需要不同的下游收集器进行求和:

List<TaxLine> flattened = taxes.stream()
    .collect(Collectors.groupingBy(
        TaxLine::getTitle,
        Collectors.groupingBy(
            TaxLine::getRate,
            Collectors.reducing(
                BigDecimal.ZERO,
                TaxLine::getPrice,
                BigDecimal::add))))
    .entrySet()
    .stream()
    .flatMap(e1 -> e1.getValue()
         .entrySet()
         .stream()
         .map(e2 -> new TaxLine(e2.getValue(), e2.getKey(), e1.getKey())))
    .collect(Collectors.toList());

这真的可以再啰嗦一点吗?-__- 谢谢你的答案哈哈 - João Menighin

2

一种方法是为你要进行分组的字段集创建一个对象。该类可以构建成提供良好助手方法的样子。

因此,使用以下方式完成原始类:

public final class TaxLine {
    private String title;
    private BigDecimal rate;
    private BigDecimal price;
    public TaxLine(String title, BigDecimal rate, BigDecimal price) {
        this.title = title;
        this.rate = rate;
        this.price = price;
    }
    public String getTitle() {
        return this.title;
    }
    public BigDecimal getRate() {
        return this.rate;
    }
    public BigDecimal getPrice() {
        return this.price;
    }
    @Override
    public String toString() {
        return "TaxLine = title:\"" + this.title + "\", rate:" + this.rate + ", price:" + this.price;
    }
}

并且分组辅助类定义如下:

public final class TaxGroup {
    private String title;
    private BigDecimal rate;
    public static TaxLine asLine(Entry<TaxGroup, BigDecimal> e) {
        return new TaxLine(e.getKey().getTitle(), e.getKey().getRate(), e.getValue());
    }
    public TaxGroup(TaxLine taxLine) {
        this.title = taxLine.getTitle();
        this.rate = taxLine.getRate();
    }
    public String getTitle() {
        return this.title;
    }
    public BigDecimal getRate() {
        return this.rate;
    }
    @Override
    public int hashCode() {
        return this.title.hashCode() * 31 + this.rate.hashCode();
    }
    @Override
    public boolean equals(Object obj) {
        if (obj == null || getClass() != obj.getClass())
            return false;
        TaxGroup that = (TaxGroup) obj;
        return (this.title.equals(that.title) && this.rate.equals(that.rate));
    }
}

将行项目合并的代码如下,分成多行以帮助查看其各个部分:

List<TaxLine> lines = Arrays.asList(
        new TaxLine("New York Tax", new BigDecimal("0.20"), new BigDecimal("20.00")),
        new TaxLine("New York Tax", new BigDecimal("0.20"), new BigDecimal("20.00")),
        new TaxLine("County Tax"  , new BigDecimal("0.10"), new BigDecimal("10.00"))
);
List<TaxLine> combined =
        lines
        .stream()
        .collect(Collectors.groupingBy(TaxGroup::new,
                                       Collectors.reducing(BigDecimal.ZERO,
                                                           TaxLine::getPrice,
                                                           BigDecimal::add)))
        .entrySet()
        .stream()
        .map(TaxGroup::asLine)
        .collect(Collectors.toList());

您可以打印输入/输出:
System.out.println("Input:");
lines.stream().forEach(System.out::println);
System.out.println("Combined:");
combined.stream().forEach(System.out::println);

要生成此内容:
Input:
TaxLine = title:"New York Tax", rate:0.20, price:20.00
TaxLine = title:"New York Tax", rate:0.20, price:20.00
TaxLine = title:"County Tax", rate:0.10, price:10.00
Combined:
TaxLine = title:"New York Tax", rate:0.20, price:40.00
TaxLine = title:"County Tax", rate:0.10, price:10.00

0
无法对分组字段进行汇总,可以通过在Taxline类内部定义一个名为TitleRate的新类来实现,如下所示。
    class Taxline{
        public static class TitleRate {
            public TitleRate(String title, int taxline) {
                ...
            }

        }

        public TitleRate getTitleRate() {
            return new TitleRate(title, taxline);
        }
    }

为了通过分组标题和税率来汇总价格,可以使用以下方法。

Map<TitleRate, List<Taxline>> groupedData = people.collect(Collectors.groupingBy(Taxline::getTitleRate));

  List<Taxline> groupedTaxLines = new ArrayList<Taxline>();
  BigDecimal groupedRate = BigDecimal.ZERO;
  for (Map<TitleRate, List<Taxline>> entry : groupedData.entrySet())
  {
    for(Taxline taxline : entry.getValue()){
        groupedRate = groupedRate.add(taxline.getPrice());
    }
    groupedTaxLines.add(new Taxline(entry.getKey().getTitle, entry.getKey().getRate(), groupedRate));
        groupedRate = BigDecimal.ZERO;
   }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接