如何根据 objects 的某些字段在列表中组合 2 个或更多 objects

How to combine 2 or more objects in a list based on certain fields of those objects

如果标题听起来含糊不清,我很抱歉。

我有一个 object -

    public class AccountInfo {
        private String accountId;
        private Double vat;
        private Double totalFee;
        private Date invoiceDate;
    }

并且有这些 objects-

的列表
List<AccountInfo> AccountInfoLst;

我想通过将 objects 与相同的 accountId 相加来操纵此列表,方法是将 vattotalFee 字段,前提是它们也具有相同的 invoiceDate

如果两个 object 具有相同的 accountId 但不同的 invoiceDate,则不应合并它们。

遍历重复的列表,将它们合并为一个 AccountInfo 并删除所有重复项。

由于日期已弃用,我们使用了 java.time.LocalDate。

代码:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class AccountInfo {
    private String accountId;

    private Double vat;
    private Double totalFee;
    private LocalDate invoiceDate;

    public Double getVat() {
        return vat;
    }

    public void setVat(Double vat) {
        this.vat = vat;
    }

    public Double getTotalFee() {
        return totalFee;
    }

    public void setTotalFee(Double totalFee) {
        this.totalFee = totalFee;
    }

    public LocalDate getInvoiceDate() {
        return invoiceDate;
    }

    public void setInvoiceDate(LocalDate invoiceDate) {
        this.invoiceDate = invoiceDate;
    }

    public AccountInfo(String accountId, Double vat, Double totalFee, LocalDate invoiceDate) {
        this.accountId = accountId;
        this.vat = vat;
        this.totalFee = totalFee;
        this.invoiceDate = invoiceDate;
    }

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public String toString() {
        return accountId + " " + vat + " " + totalFee + " " + invoiceDate;
    }

    public boolean areEquals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AccountInfo that = (AccountInfo) o;
        var thatInvoiceDate = that.getInvoiceDate();
        var thisInvoiceDate = getInvoiceDate();
        return that.getAccountId().equals(getAccountId()) && thatInvoiceDate.isEqual(thisInvoiceDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(accountId, vat, totalFee, invoiceDate);
    }
}

public class MainClass {
    public static void main(String... $) {
        var out = System.out;
        
        List<AccountInfo> accounts = Stream.of(new AccountInfo("A", 20.2, 200.4, LocalDate.of(2022, 11, 3))
                , new AccountInfo("A", 20.2, 200.4, LocalDate.of(2022, 11, 3)),
                new AccountInfo("B", 20.2, 200.4, LocalDate.of(2023, 11, 2)),
                new AccountInfo("B", 20.2, 200.4, LocalDate.of(2023, 11, 2))).collect(Collectors.toList());
        
        //Iterating over all the accounts
        for (int i = 0; i < accounts.size(); i++) {
            var account = accounts.get(i);
            ArrayList<AccountInfo> duplicates = new ArrayList<>();
            for (int j = i + 1; j < accounts.size(); j++) {
                var acc = accounts.get(j);
                if (acc.areEquals(account))
                    duplicates.add(acc);
            }
//            out.println(i + " " + indices);
            //Merging all duplicates to account
            for (AccountInfo acc : duplicates) {
                account.setTotalFee(acc.getTotalFee() + account.getTotalFee());
                account.setVat(acc.getVat() + account.getVat());
            }
            //Removing all duplicates
            accounts.removeAll(duplicates);
        }
        //printing accounts after removal of duplicates
        out.println(accounts);
    }
}

输出:

[A 40.4 400.8 2022-11-03, B 40.4 400.8 2023-11-02]

如果您想利用 Streams 实现,一种可能的方法是将 collect 数据放入 Map,用作键组合 accountIdinvoiceDate.

的对象

映射到相同密钥的所有帐户合并

accountIdinvoiceDate 不同 的帐户将保持不变。

另一种选择是创建嵌套地图。其中 accountId 将用作键,值将是一个映射,该映射将依次使用 invoiceDate 作为键,值将是 AccountInfo.

    public static void main(String[] args) {
        List<AccountInfo> result = List.of(
                    new AccountInfo("123", 80., 180., LocalDate.now()),
                    new AccountInfo("123", 120., 590., LocalDate.now()),
                    new AccountInfo("123", 30., 120., LocalDate.now().minusDays(3))
                )
                .stream()
                .collect(Collectors.toMap(
                        AccountWrapper::new,
                        UnaryOperator.identity(),
                        (acc1, acc2) -> new AccountInfo(
                                acc1.getAccountId(),
                                acc1.getVat() + acc2.getVat(),
                                acc1.getTotalFee() + acc2.getTotalFee(),
                                acc1.getInvoiceDate())
                ))
                .values().stream()
                .collect(Collectors.toList());

        result.forEach(System.out::println);
    }



    public record AccountWrapper(String accountId, LocalDate invoiceDate) {
        public AccountWrapper(AccountInfo info) {
            this(info.getAccountId(), info.getInvoiceDate());
        }
    }

如果您使用 Java 15 或更早版本 AccountWrapper 可以作为 常规 class 实现:

    public class AccountWrapper {
        private final String accountId;
        private final LocalDate invoiceDate;

        public AccountWrapper(AccountInfo info) {
            this.accountId = info.getAccountId();
            this.invoiceDate = info.getInvoiceDate();
        }

        @Override
        public int hashCode() {
            return Objects.hash(accountId, invoiceDate);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AccountWrapper other = (AccountWrapper) o;
            return accountId.equals(other.accountId) && invoiceDate.equals(other.invoiceDate);
        }
    }

带有嵌套地图的版本,不需要辅助对象

        List<AccountInfo> result = List.of(
                        new AccountInfo("123", 80., 180., LocalDate.now()),
                        new AccountInfo("123", 120., 590., LocalDate.now()),
                        new AccountInfo("123", 30., 120., LocalDate.now().minusDays(3))
                )
                .stream()
                .collect(Collectors.groupingBy(
                        AccountInfo::getAccountId,
                        Collectors.toMap(
                                AccountInfo::getInvoiceDate,
                                UnaryOperator.identity(),
                                (acc1, acc2) -> new AccountInfo(
                                    acc1.getAccountId(),
                                    acc1.getVat() + acc2.getVat(),
                                    acc1.getTotalFee() + acc2.getTotalFee(),
                                    acc1.getInvoiceDate())
                        )))
                .values().stream()
                .flatMap(map -> map.values().stream())
                .collect(Collectors.toList());

        result.forEach(System.out::println);

输出

AccountInfo{accountId='123', vat=200.0, totalFee=770.0, invoiceDate=2022-02-09}
AccountInfo{accountId='123', vat=30.0, totalFee=120.0, invoiceDate=2022-02-06}