有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Java8中并行使用BigDecimal进行分组和求和

我有一个产品列表,其中有一个属性。该列表可以包含公共产品名称,而其他属性则不同。因此,我想使用分组和求和,按产品和在Java8中共享公共名称的产品数量的总和对列表进行分组

Example:
[  
   {  
      "name":"Product A",
      "amount":"40.00",
      "description":"Info1",
      "number":"65"
   },
   {  
      "name":"Product A",
      "amount":"50.00",
      "description":"Info2",
      "number":"67"
   },
   {  
      "name":"Product A",
      "amount":"100.00",
      "description":"Info3",
      "number":"87"
   },
   {  
      "name":"Product B",
      "amount":"45.00",
      "description":"Info4",
      "number":"86"
   },
   {  
      "name":"Product D",
      "amount":"459.00",
      "description":"Info5",
      "number":"7"
   },
   {  
      "name":"Product B",
      "amount":"50.00",
      "description":"Info6",
      "number":"8"
   }
]

输出应与此类似:

{  
   "Product A = 190.00":[  
      {  
         "name":"Product A",
         "amount":"40.00",
         "description":"Info1",
         "number":"65"
      },
      {  
         "name":"Product A",
         "amount":"50.00",
         "description":"Info2",
         "number":"67"
      },
      {  
         "name":"Product A",
         "amount":"100.00",
         "description":"Info3",
         "number":"87"
      }
   ],
   "Product B=95.00":[  
      {  
         "name":"Product B",
         "amount":"45.00",
         "description":"Info4",
         "number":"86"
      },
      {  
         "name":"Product B",
         "amount":"50.00",
         "description":"Info6",
         "number":"8"
      }
   ],
   "Product D = 459.00":[  
      {  
         "name":"Product D",
         "amount":"459.00",
         "description":"Info5",
         "number":"7"
      }
   ]

我已经创建了一个bean类ProductBean,它包含所有字段(名称、数量、描述和编号)以及相同的getter和setter。 productBeans有所有产品的列表

Map<String, List<ProductBean>> groupByProduct = 
               productBeans.stream()
                     .collect(Collectors.groupingBy(item -> item.getName()))

Map<String, BigDecimal> result = 
      productBeans.stream()
             .collect(Collectors.groupingBy(ProductBean::getName, 
                                Collectors.mapping(ProductBean::getAmount, 
                     Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));

groupByProduct具有按名称分组的产品列表。 结果将产品图作为键,将该产品的总量作为值

但在这里,我试图将产品和总量映射到产品列表中。我试图结合上面的代码来获得预期的输出,但未能实现。通过在地图上迭代,也可以实现同样的效果

但是,在合并上述代码以获得产品名称和金额作为键、列表作为值的映射时,任何帮助都是有帮助的


共 (3) 个答案

  1. # 1 楼答案

    似乎您需要这样的东西Map<String, BigDecimal, List<ProductBean>>,其中字符串表示nameBigDecimal是所有产品数量的总和,List<ProductBean>是该特定组中的产品列表

    这样的映射是不可能的,因此,您已经设法分两个步骤来完成它,这不是非常有效,而且它不会将相关数据保存在一起,而是将其分为两个映射

    我的建议是创建这样一个包装器类:

    class ResultSet {
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public BigDecimal getAmount() {
            return amount;
        }
    
        public void setAmount(BigDecimal amount) {
            this.amount = amount;
        }
    
        public List<ProductBean> getProductBeans() {
            return productBeans;
        }
    
        public void setProductBeans(List<ProductBean> productBeans) {
            this.productBeans = productBeans;
        }
    
        private String name;
        private BigDecimal amount;
        private List<ProductBean> productBeans;
    
        @Override
        public String toString() {
            return "ResultSet{" +
                    "name='" + name + '\'' +
                    ", amount=" + amount +
                    ", productBeans=" + productBeans +
                    '}';
        }
    }
    

    它将封装一组给定的ProductBean's维护相关数据,以提高可维护性和可读性

    现在,您可以通过以下方式完成手头的任务:

    List<ResultSet> result = productList.stream()
                    .collect(Collectors.groupingBy(ProductBean::getName))
                    .entrySet()
                    .stream()
                    .map(e -> {
                        ResultSet resultSet = new ResultSet();
                        BigDecimal sum = e.getValue().stream()
                                .map(ProductBean::getAmount)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        resultSet.setName(e.getKey());
                        resultSet.setAmount(sum);
                        resultSet.setProductBeans(e.getValue());
                        return resultSet;
                    }).collect(Collectors.toList());
    

    这将按ProductBean名称分组,然后将每个组映射到一个ResultSet实例,该实例将封装该名称、该特定组中的金额总和以及该组中的整个ProductBean's

    通过为映射创建一个方法,可以进一步重构上述流查询:

    private static ResultSet mapToResultSet(Map.Entry<String, List<ProductBean>> e) {
            ResultSet resultSet = new ResultSet();
            BigDecimal sum = e.getValue().stream()
                    .map(ProductBean::getAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            resultSet.setName(e.getKey());
            resultSet.setAmount(sum);
            resultSet.setProductBeans(e.getValue());
            return resultSet;
    }
    

    因此,将流查询呈现为:

    List<ResultSet> result = productList.stream()
                    .collect(Collectors.groupingBy(ProductBean::getName))
                    .entrySet()
                    .stream()
                    .map(Main::mapToResultSet) // Main representing the class containing the mapToResultSet method
                    .collect(Collectors.toList());
    
  2. # 2 楼答案

    嗯,你几乎明白了

    Collectors.groupingBy允许使用第二个参数,该参数是将为键生成值的收集器。您只需从第二行将收集器作为第二个参数传递:

        Map<String, BigDecimal> result = productBeans
                .stream()
                .collect(
                    Collectors.groupingBy(ProductBean::getName,
                    Collectors.mapping(ProductBean::getAmount, 
                        Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
    
  3. # 3 楼答案

    除了我前面的答案,我建议创建一个包装器类。如果出于任何原因,不想单独创建一个自定义类来包装结果,那么另一个解决方案如下:

    List<AbstractMap.SimpleEntry<Map.Entry<String, List<ProductBean>>, BigDecimal>> 
             resultSet =
                    productBeans.stream()
                                .collect(Collectors.groupingBy(ProductBean::getName))
                                .entrySet()
                                .stream()
                                .map(Main::mapToSimpleEntry)
                                .collect(Collectors.toList());
    

    其中Main是包含mapToSimpleEntry的类,并且mapToSimpleEntry定义为:

    private static AbstractMap.SimpleEntry<Map.Entry<String, List<ProductBean>>, BigDecimal> mapToSimpleEntry(Map.Entry<String, List<ProductBean>> e) {
            BigDecimal sum =
                    e.getValue().stream()
                            .map(ProductBean::getAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
            return new AbstractMap.SimpleEntry<>(e, sum);
    }
    

    此解决方案的优点是:

    1. 不需要创建自定义类
    2. 更少的代码