嵌套POJO列表的流转到另一个POJO列表

4

希望有人能帮助我。我有一个具有以下结构的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*/
}

我要做的是将一个发票(Invoice)List按照VAT组进行分组,输出一个VAT类型(VatType)List。就像在SQL中的DISTINCT语句一样。 比如我有以下的List:
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);

预期结果应该是一个 List,其中包含以下项目:VatType
("S1",300.0,90.0)
("S2",380.0,90.0)
("A1",150.0,30.0)

如何使用一个stream一次性获取此列表。避免通过Lists进行迭代?提前致谢。


你有检查过Stream API吗? - Alberto S.
1
从“InvoiceDetailes”和“VatType”中提取公共部分到“CommonClass”可能是一个好主意,这样您就可以对“List<CommonClass>”执行操作,而不必反复转换列表。 - Balázs Nemes
4个回答

2
您首先需要使用flatMap创建一个流,以获取列表中所有InvoiceInvoiceDetails。之后,您可以使用toMap的变体进行归约,该变体带有合并方法。最后,Mapvalues()方法将获得VatTypeCollection
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);

1
你可以使用Java流功能:

Function

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,虽然它没有“合并”值,但它运行得很好。 - pburgov

1

以下是一种使用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实例和一个merge方法,可以将两个VatType实例组合起来:
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;
}

然后,你可以将第一个片段简化为:

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());

1
谢谢Federico,这是一种非常有趣的流处理的替代方式。 - pburgov

0

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


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