如何根据 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 相加来操纵此列表,方法是将 vat 和 totalFee 字段,前提是它们也具有相同的 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
,用作键组合 accountId
和 invoiceDate
.
的对象
映射到相同密钥的所有帐户将合并。
在 accountId
或 invoiceDate
中 不同 的帐户将保持不变。
另一种选择是创建嵌套地图。其中 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}
如果标题听起来含糊不清,我很抱歉。
我有一个 object -
public class AccountInfo {
private String accountId;
private Double vat;
private Double totalFee;
private Date invoiceDate;
}
并且有这些 objects-
的列表List<AccountInfo> AccountInfoLst;
我想通过将 objects 与相同的 accountId 相加来操纵此列表,方法是将 vat 和 totalFee 字段,前提是它们也具有相同的 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
,用作键组合 accountId
和 invoiceDate
.
映射到相同密钥的所有帐户将合并。
在 accountId
或 invoiceDate
中 不同 的帐户将保持不变。
另一种选择是创建嵌套地图。其中 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}