删除Java8中的重复对象,得到对象中String值的总和(第一个值需要在BigDecimal中转换)

Remove duplicate object in Java 8 and get the sum of String values in the object (first value need to convert in BigDecimal)

我有 class DTO,其中有 3 个字段。

我有一个 DTO 对象列表。

我想根据列表中的ID字段删除所有重复的对象,并得到字符串类型的值的总和。第一个值 转换为 Bigdecimal 然后添加(求和)重复的对象值。

注意:: 值是 class DTO

中的字符串格式
@AllArgsConstructor
static class DTO {
    int id;
    int year;
    String value;
}

List<DTO> list = new ArrayList();
list.add(new DTO(1, 2020, "5.5"));
list.add(new DTO(1, 2020, "-8.0"));
list.add(new DTO(2, 2020, "1.5"));
list.add(new DTO(3, 2020, "4.5"));
list.add(new DTO(3, 2020, "1.5"));
list.add(new DTO(3, 2020, "-9.5"));
list.add(new DTO(4, 2020, "-3.5"));
list.add(new DTO(4, 2020, "7.5"));
list.add(new DTO(4, 2020, "5.5"));
list.add(new DTO(4, 2020, "-7.5"));

我尝试了各种流、收集器、groupby 等,但我迷路了。

这是我目前拥有的:

list.stream().collect(Collectors.groupingBy(e -> e.getId()), Collectors.reducing(BigDecimal.ZERO, DTO::getValue, BigDecimal.add()));

预计

DTO(1, 2021, "-2.5"); (value Sum of group 1)
DTO(2, 2021, "-1.5"); (value Sum of group 2)
DTO(3, 2021, "-3.5"); (value Sum of group 3)
DTO(4, 2021, "2"); (value Sum of group 4)

您可以创建一个添加两个 DTO:

的方法
private static DTO add(DTO dto1, DTO dto2) {
    BigDecimal bd1 = new BigDecimal(dto1.getValue());
    BigDecimal bd2 = new BigDecimal(dto2.getValue());
    return new DTO(dto1.getId(), dto1.getYear(), bd1.add(bd2).toString());
}

然后你可以 streamid 分组并使用以前的方法减少:

private static List<DTO> add(List<DTO> list) {
    Map<Integer, Optional<DTO>> map = list.stream()
        .collect(Collectors.groupingBy(DTO::getId, 
                Collectors.reducing((d1, d2) -> add(d1, d2))));

    return map.values().stream()
            .filter(Optional::isPresent)
            .map(Optional::get).toList();
}

测试:

List<DTO> list = List.of(
        new DTO(1, 2020, "5.5"),
        new DTO(1, 2020, "-8.0"),
        new DTO(2, 2020, "1.5"),
        new DTO(3, 2020, "4.5"),
        new DTO(3, 2020, "1.5"),
        new DTO(3, 2020, "-9.5"),
        new DTO(4, 2020, "-3.5"),
        new DTO(4, 2020, "7.5"),
        new DTO(4, 2020, "5.5"),
        new DTO(4, 2020, "-7.5"));

List<DTO> listAdded = add(list);
    
listAdded.forEach(System.out::println);

输出:

DTO[1, 2020, -2.5]
DTO[2, 2020, 1.5]
DTO[3, 2020, -3.5]
DTO[4, 2020, 2.0]

如果我这样做,我不会使用流,而是使用合并方法,该方法采用当前值并将其应用于现有值,在本例中是保存总和的实例。

List<DTO> list = List.of(new DTO(1, 2020, "5.5"),
        new DTO(1, 2020, "-8.0"), new DTO(2, 2020, "1.5"),
        new DTO(3, 2020, "4.5"), new DTO(3, 2020, "1.5"),
        new DTO(3, 2020, "-9.5"), new DTO(4, 2020, "-3.5"),
        new DTO(4, 2020, "7.5"), new DTO(4, 2020, "5.5"),
        new DTO(4, 2020, "-7.5"));

Map<String, DTO> map = new HashMap<>();

for (DTO dto : list) {
    map.merge(dto.getId() + "_" + dto.getYear(), dto, (sum, dt) -> new DTO(
            sum.getId(), sum.getYear(),
            new BigDecimal(sum.getValue())
                    .add(new BigDecimal((dt.getValue())))
                    .toString()));
}

map.values().forEach(System.out::println);

打印

DTO[1, 2020, -2.5]
DTO[2, 2020, 1.5]
DTO[3, 2020, -3.5]
DTO[4, 2020, 2.0]

如果您想将结果放入列表中,您可以这样做。

List<DTO> newList = new ArrayList<>(map.values());

除了 getter 和 setter 之外,我还使用以下 toString 进行输出。

@Override
public String toString() {
        return "%s[%d, %d, %s]".formatted(this.getClass().getSimpleName(),id, year, value);
}

但如果你真的想使用流,那么你可以这样做。

  • 流式传输对象
  • 使用 toMap 收集
  • 并使用 toMap 的合并方法对值求和。
Map<String, DTO> result = list.stream().collect(Collectors
        .toMap(dto->dto.getId()+"_"+dto.getYear(), dto -> dto, (sum, dto) -> new DTO(
                sum.getId(), sum.getYear(),
                new BigDecimal(sum.getValue())
                        .add(new BigDecimal((dto.getValue())))
                        .toString())));