【问题标题】:Filtering by multiple values and Summing Up object in a List in Java [duplicate]在Java列表中按多个值过滤并汇总对象[重复]
【发布时间】:2021-07-29 09:04:13
【问题描述】:

我有一个List<MyObject>,这个列表包含数千个对象。我想通过recordNoyear 过滤这个列表。如果有任何对象具有相同的recordNoyear 值,我想总结它们的amountsum

过滤列表并汇总值后,我想创建一个新的List<MyObject>,并且此列表不会包含任何重复的对象(recordNoyear

MyObject 结构:

@Getter
@Setter
public class MyObject{

    private Long recordNo;
    private Integer year;
    private BigDecimal amount;
    private BigDecimal sum;
}

当前示例列表输出:

[
    {
     "recordNo": 10,
     "year": 2021,
     "amount": 10,
     "sum": 100,
    },
    {
     "recordNo": 10,
     "year": 2021,
     "amount": 20,
     "sum": 200,
    },
    {
     "recordNo": 10,
     "year": 2020,
     "amount": 20,
     "sum": 100,
    },
    {
     "recordNo": 10,
     "year": 2020,
     "amount": 5,
     "sum": 20,
    },
    {
     "recordNo": 11,
     "year": 2021,
     "amount": 10,
     "sum": 200,
    }
]

所需的列表输出:

[
    {
     "recordNo": 10,
     "year": 2021,
     "amount": 30,
     "sum": 300,
    },
    {
     "recordNo": 10,
     "year": 2020,
     "amount": 25,
     "sum": 120,
    },
    {
     "recordNo": 11,
     "year": 2021,
     "amount": 10,
     "sum": 200,
    }
]

创建所需列表输出的最佳方法是什么?

【问题讨论】:

    标签: java java-8


    【解决方案1】:

    一个简单的解决方案基于 Stream API Collectors::toMap 和合并函数和 Supplier<Map> 来维护初始流中元素的顺序如下 - 计算映射的值被包装到新的 ArrayList:

    List<MyObject> totals = new ArrayList<>(
        data               // initial List<MyObject> read from JSON
        .stream()          // Stream<MyObject>
        .collect(Collectors.toMap(
            obj -> Arrays.asList(obj.getRecordNo(), obj.getYear()), // group by recordNo and year
            obj -> obj,    // a copy may be created if needed
            (o1, o2) -> {  // merge function
                o1.setAmount(o1.getAmount().add(o2.getAmount())); // total amount
                o1.setSum(o1.getSum().add(o2.getSum()));          // total sum
    
                return o1;
            },
            LinkedHashMap::new // maintain order of initial data
        ))
        .values() // get Collection<MyObject> with totals grouped by key
    );
    

    对于像这样创建以匹配输入 JSON 的 data

    List<MyObject> data = Arrays.asList(
        new MyObject(10L, 2021, new BigDecimal(10), new BigDecimal(100)),
        new MyObject(10L, 2021, new BigDecimal(20), new BigDecimal(200)),
        new MyObject(10L, 2020, new BigDecimal(20), new BigDecimal(100)),
        new MyObject(10L, 2020, new BigDecimal(5), new BigDecimal(20)),
        new MyObject(11L, 2021, new BigDecimal(10), new BigDecimal(200)) 
    );
    

    结果如下(在MyObject类中使用自定义toString):

    {recordNo: 10, year: 2021, amount:30, sum:300}
    {recordNo: 10, year: 2020, amount:25, sum:120}
    {recordNo: 11, year: 2021, amount:10, sum:200}
    

    【讨论】:

      【解决方案2】:

      使用流的解决方案:

         public static void main(String[] args) {
              // create some test data
              List<MyObject> objects  =  new ArrayList<>();
              for (int i = 0; i < 3; i++) {
                  MyObject obj = new MyObject();
                  obj.setSum(BigDecimal.TEN);
                  obj.setAmount(new BigDecimal(i));
                  obj.setYear(2020);
                  obj.setRecordNo(1L);
                  objects.add(obj);
              }
      
              for (int i = 0; i < 3; i++) {
                  MyObject obj = new MyObject();
                  obj.setSum(BigDecimal.TEN);
                  obj.setAmount(new BigDecimal(i));
                  obj.setYear(2021);
                  obj.setRecordNo(2L);
                  objects.add(obj);
              }
      
              for (int i = 0; i < 3; i++) {
                  MyObject obj = new MyObject();
                  obj.setSum(BigDecimal.TEN);
                  obj.setAmount(new BigDecimal(i));
                  obj.setYear(2021);
                  obj.setRecordNo(3L);
                  objects.add(obj);
              }
      
      
              // groupingBy collector produces a key for object grouping
              // we group by 'recordNo' and 'year' fields
              Map<String, List<MyObject>> grouped = objects.stream()
                      .collect(Collectors.groupingBy(obj -> obj.getRecordNo() + " " + obj.getYear()));
      
      
              // groupingBy colletor returned a map, but we need only
              // its values
              List<MyObject> groupedAndSummedUp = grouped.values()
                      .stream()
                      .map(group -> {
                          return group.stream()
                                  .reduce((obj1, obj2) -> {
                                      // actual 'merging' of objects is happening here
                                      BigDecimal newAmount = obj1.getAmount().add(obj2.getAmount());
                                      BigDecimal newSum = obj1.getSum().add(obj2.getSum());
                                      obj1.setAmount(newAmount);
                                      obj1.setSum(newSum);
                                      return obj1;
                                  })
                                  .get();
                      })
                      .collect(Collectors.toList());
      

      【讨论】:

        【解决方案3】:

        您可以使用Comparator 对列表进行排序。创建一个实现比较器的类(MyComparator),添加根据您的条件排序的逻辑,例如首先基于recordNo,然后基于year(您可以决定升序或降序)。

        然后你可以调用Collections.sort(&lt;yourList&gt;, new MyComparator());它会对当前列表进行排序。

        现在您可以根据需要创建一个新列表,并继续检查是否需要将先前对象的值与当前对象相加。如果它是真的,您可以使用汇总的值创建一个新对象并将其放入新列表中。

        附:由于您没有提供任何工作逻辑,因此我自己没有提供代码。

        【讨论】:

          【解决方案4】:

          不完全确定你最后想要做什么,但你可以查看 Java8 流,这将过滤掉你不想要的对象,只留下你想要的项目

          Stream.of(list or array).filter(obj->condition);
          

          ref

          【讨论】:

            【解决方案5】:

            让我们从迭代(非流)方法开始

            public static void main(String[] args) {
            
                List<MyObject> objects = List.of(
                        new MyObject(10L, 2021, new BigDecimal(10), new BigDecimal(100)),
                        new MyObject(10L, 2021, new BigDecimal(20), new BigDecimal(200)),
                        new MyObject(10L, 2020, new BigDecimal(20), new BigDecimal(100)),
                        new MyObject(10L, 2020, new BigDecimal(5), new BigDecimal(20)),
                        new MyObject(11L, 2021, new BigDecimal(10), new BigDecimal(200))
                );
            
                Map<Long, Map<Integer, MyObject>> mapped = new HashMap<>();
            
                for (MyObject object : objects) {
                    var recordNo = object.getRecordNo();
            
                    if (mapped.containsKey(recordNo)) {
                        var objectMap = mapped.get(recordNo);
                        var year = object.getYear();
                        if (objectMap.containsKey(year)) {
                            var objectFromMap = objectMap.get(year);
                            var myObject = new MyObject(recordNo, year, object.getAmount().add(objectFromMap.getAmount()), object.getSum().add(objectFromMap.getSum()));
                            objectMap.put(year, myObject);
                        } else {
                            objectMap.put(year, object);
                        }
                        mapped.put(recordNo, objectMap);
                    } else {
                        Map<Integer, MyObject> value = new HashMap<>();
                        value.put(object.getYear(), object);
                        mapped.put(recordNo, value);
                    }
                }
            
                // convert map to lists
                var newList = mapped.values()
                        .stream()
                        .map(a -> new ArrayList<>(a.values()))
                        .collect(Collectors.toList());
            
                System.out.println(newList);
            }
            

            还有一种使用流的方法

            var collect = objects.stream()
                    .collect(Collectors.groupingBy(
                            MyObject::getRecordNo,
                            Collectors.groupingBy(MyObject::getYear)))
                    .values()
                    .stream()
                    .map(a -> a.values()
                            .stream()
                            .map(values -> values.stream()
                                    .reduce((myObject1, myObject2) -> {
                                        Long recordNo = myObject1.getRecordNo();
                                        Integer year = myObject1.getYear();
            
                                        var newAmount = myObject1.getAmount().add(myObject2.getAmount());
                                        var newSum = myObject1.getSum().add(myObject2.getSum());
            
                                        return new MyObject(recordNo, year, newAmount, newSum);
                                    })
                                    .get())
                            .collect(Collectors.toList()))
                    .collect(Collectors.toList());
            
            System.out.println(collect);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-11-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-02-06
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多