【问题标题】:Stream of a nested POJOs List to a another POJO LIST嵌套 POJO 列表到另一个 POJO 列表的流
【发布时间】:2017-06-20 11:42:21
【问题描述】:

希望有人可以帮助我。我有一个具有以下结构的 POJO:

public class Invoice{

private String docNum;
private String customer;
private ArrayList<InvoiceDetails> invoiceDetails;

/* Getters and setters*/
}

另外一个 POJO 如下

public class InvoiceDetails{

private String vatGroup;
private Double vatAmount;
private Double amount;

/* Getters and setters*/
}

除了我有以下第三个

public class VatType{

private String vatGroup;
private Double vatAmount;
private Double amount;

/* Getters and setters*/
}

我要做的是将InvoiceList 减少为按vatGroup 分组的ListVatType。就像SQL 中的DISTINCT 子句一样。 假设我有以下列表:

InvoiceDetails idA1 = new InvoiceDetails("S1", 100.0, 40.0);
InvoiceDetails idA2 = new InvoiceDetails("S2", 140.0, 40.0);
InvoiceDetails idA3 = new InvoiceDetails("A1", 50.0, 10.0);
ArrayList<InvoiceDetails> listA = new ArrayList<>();
listA.add(idA1);
listA.add(idA2);
listA.add(idA3);

Invoice invoiceA = new Invoice();
invoiceA.setDetailList(listA);

InvoiceDetails idB1 = new InvoiceDetails("S1", 200.0, 50.0);
InvoiceDetails idB2 = new InvoiceDetails("S2", 240.0, 50.0);
InvoiceDetails idB2 = new InvoiceDetails("A1", 100.0, 20.0);
ArrayList<InvoiceDetails> listB = new ArrayList<>();
listB.add(idB1);
listB.add(idB2);
listB.add(idB3);


Invoice invoiceB = new Invoice();
invoiceB.setDetailList(listB);

List<Invoice> invoiceList = new ArrayList<>();
invoiceList.add(invoiceA);
invoiceList.add(invoiceB);

预期的结果是Listof VatType,包含以下项目:

("S1",300.0,90.0)
("S2",380.0,90.0)
("A1",150.0,30.0)

如何一次性使用stream 获取此列表。避免遍历Lists? 提前致谢

【问题讨论】:

  • 你检查过 Stream API 吗?
  • InvoiceDetailesVatType中的公共部分提取到CommonClass中可能是个好主意,然后您可以对List&lt;CommonClass&gt;进行操作而不必转换列表 vica-反之亦然?

标签: java list java-8 java-stream pojo


【解决方案1】:

您首先需要一个flatMap 来创建列表中所有InvoiceDetails 和所有Invoice 的流。之后,您可以使用获得合并方法的toMap 变体来减少。最后Mapvalues() 方法将得到CollectionVatType

Collection<VatType> values = invoiceList.stream()
        .flatMap(invoices -> invoices.getInvoiceDetails().stream())
        .collect(toMap(InvoiceDetails::getVatGroup, VatType::new, (i1, i2) -> {
            i1.setAmount(i1.getAmount() + i2.getAmount());
            i1.setVatAmount(i1.getVatAmount() + i2.getVatAmount());
            return i1;
        }))
        .values();

VatType 构造函数的使用位置:

VatType(InvoiceDetails invoiceDetails) {
    vatGroup = invoiceDetails.getVatGroup();
    vatAmount = invoiceDetails.getVatAmount();
    amount = invoiceDetails.getAmount();
}

如果需要,您可以轻松地从 Collection 创建一个 List

List<VatType> vatTypes = new ArrayList<>(values);

【讨论】:

    【解决方案2】:

    可以使用Java流函数:

    Function<InvoiceDetails, VatType> myF = t -> new VatType(t.getVatGroup(), t.getVatAmount(), t.getAmount());
    

    然后比使用流式传输:

    List<VatType> myLocations = 
            invoiceList.stream()
            .map(Invoice::getInvoiceDetails)
            .flatMap(Collection::stream)
            .map(myF)
            .collect(Collectors.<VatType>toList());
    

    【讨论】:

    • 谢谢 ΦXocę 웃 Пepeúpa 虽然它没有合并这些值,但它工作得很好。
    【解决方案3】:

    这是一种使用 Java 8 的新功能 ListMap 的方法,无需流:

    Map<String, VatType> map = new HashMap<>();
    
    invoiceList.forEach(i -> i.getDetailList().forEach(d ->
        map.merge(
            d.getVatGroup(),
            new VatType(d.getVatGroup(), d.getVatAmount(), d.getAmount()),
            (left, right) -> {
                left.setAmount(left.getAmount() + right.getAmount());
                left.setVatAmount(left.getVatAmount() + right.getVatAmount());
                return left;
            })));
    
    List<VatType> result = new ArrayList<>(map.values());
    

    如果您可以向VatType 添加一个构造函数,该构造函数接受一个InvoiceDetail 实例和一个组合两个VatType 实例的merge 方法:

    public VatType(String vatGroup, Double vatAmount, Double amount) {
        this.vatGroup = vatGroup;
        this.vatAmount = vatAmount;
        this.amount = amount;
    }
    
    public VatType(InvoiceDetails details) {
        this(details.getVatGroup(), details.getVatAmount(), details.getAmount());
    }
    
    public VatType merge(VatType another) {
        this.vatAmount += another.vatAmount;
        this.amount += another.amount;
        return this;
    }
    

    然后,您可以将第一个 sn-p 简化为:

    Map<String, VatType> map = new HashMap<>();
    
    invoiceList.forEach(i -> i.getDetailList().forEach(d ->
            map.merge(d.getVatGroup(), new VatType(d), VatType::merge)));
    
    List<VatType> result = new ArrayList<>(map.values());
    

    【讨论】:

    • 谢谢 Federico 这是一个非常有趣的流的替代方式。
    【解决方案4】:

    如果这是您项目中的常见任务,您也可以使用dozer library

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-08
      相关资源
      最近更新 更多