Java 8 - 用 GroupBy 总和替换每一行

Java 8 - Replace each row with GroupBy sum

我有 class 数据,它有 4 个字段并有值列表,然后我想用 java 8 和流替换每一行按年份分组并求和同一年份的值。请看下面的代码和输出。

class Data {
    int id;
    int year;
    String name;
    double value;
}

List<Data> list = new ArrayList<>();
list.add(new Data(101, 2018, "AAA", 20));
list.add(new Data(102, 2019, "BBB", 30));
list.add(new Data(103, 2020, "CCC", 10));
list.add(new Data(104, 2019, "DDD", 50));
list.add(new Data(105, 2020, "EEE", 40));
list.add(new Data(106, 2020, "FFF", 60));

第一个代码尝试:

list.stream().collect(Collectors.groupingBy(Data::getYear, Collectors
    .summingDouble(Data::getValue)));

第二个代码尝试:

list.stream().collect(Collectors.toMap(value -> value.getYear(), Function.identity(),
            (a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() + b.getValue()))).values()
            .forEach(value -> System.out.println(value));

输出:

id    year   name   value
101   2018   "AAA"   20
102   2019   "BBB"   80
103   2020   "CCC"   110

预期输出:

id    year   name   value
101   2018   "AAA"   20
102   2019   "BBB"   80
103   2020   "CCC"   110
104   2019   "DDD"   80
105   2020   "EEE"   110
106   2020   "FFF"   110 

地图由您的代码中的第二个流创建,每年 preserves only a single entry

重映射函数 (a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() + b.getValue())) 处理重复项,以便每个数据对象的值将是所有值的总和。

为了 retain all 给定年份的 Datachange the value of every object to be a total value,您必须采取几个步骤:

  • 创建地图:year --> total value per year(已正确完成);
  • 创建地图:year --> list of Data 个对象;
  • total value per year 应用于每个 Data 对象。
    public static void main(String[] args) {
        List<Data> dataList = List.of(
                new Data(101, 2018, "AAA", 20),
                new Data(102, 2019, "BBB", 30),
                new Data(103, 2020, "CCC", 10),
                new Data(104, 2019, "DDD", 50),
                new Data(105, 2020, "EEE", 40),
                new Data(106, 2020, "FFF", 60)
        );

        Map<Integer, Double> yearToTotalVal = getTotalValueMap(dataList);

        Map<Integer, List<Data>> yearToData = getYearToDataListMap(dataList);

        appllyTotalValue(yearToTotalVal, yearToData);

        for (Map.Entry<Integer, List<Data>> entry: yearToData.entrySet()) {
            System.out.println(entry);
        }
    }
    private static Map<Integer, Double> getTotalValueMap(List<Data> dataList) {
        return dataList.stream()
                .collect(Collectors.groupingBy(Data::getYear,
                                    Collectors.summingDouble(Data::getValue)));
    }

    private static Map<Integer, List<Data>> getYearToDataListMap(List<Data> dataList) {
        return dataList.stream()
                .collect(Collectors.groupingBy(Data::getYear));
    }
    private static void appllyTotalValue(Map<Integer, Double> yearToTotalVal, 
                                         Map<Integer, List<Data>> yearToData) {
        for (Integer year: yearToTotalVal.keySet()) {
            yearToData.get(year)
                    .replaceAll(data -> new Data(data.getId(),
                                                data.getYear(),
                                                data.getName(),
                                                yearToTotalVal.get(year)));
        }
    }

输出 - 每年的数据:

2018=[Data{id=101, year=2018, name='AAA', value=20.0}]
2019=[Data{id=102, year=2019, name='BBB', value=80.0}, Data{id=104, year=2019, name='DDD', value=80.0}]
2020=[Data{id=103, year=2020, name='CCC', value=110.0}, Data{id=105, year=2020, name='EEE', value=110.0}, Data{id=106, year=2020, name='FFF', value=110.0}]

旁注:

  • 我还建议你制作 Data class immutable
    public class Data {
        private final int id;
        private final int year;
        private final String name;
        private final double value;

        // Constructor, getters, etc
    }