实现效果:

List<Cat> cats = new ArrayList<>();
cats.add(new Cat("aaa", 1, 2));
cats.add(new Cat("bbb", 5, 6));
cats.add(new Cat("aaa", 3, 4));
cats.add(new Cat("bbb", 7, 8));

"aaa", 4, 6
"bbb", 12, 14

运行环境:jdk8

    Map<String, Double> map1 = values.stream().collect(Collectors.groupingBy(EActual::getL2, Collectors.summingDouble(k -> Double.parseDouble(k.getSalaryValueE()))));

利用Stream流对集合中的字段进行汇总求和,目前提供的API仅支持单列求和,不满足日常使用,提供以下两种方式:

方式一:反射实现

    /**
     * 计算List中各列之和
     * @param dataList 数据集合
     * @param clazz 对象
     * @param includeList 要计算的列
     * @return
     * @param <T>
     * @throws Exception
     */
    public static <T> T getSumByList(List<T> dataList, Class<T> clazz, List<String> includeList) throws Exception {
        if (includeList == null) {
            throw new RuntimeException("不能为null,至少为空集合");
        }

        Map<String, Object> map = new HashMap<>();
        Field[] declaredFields = clazz.getDeclaredFields();
        //遍历数据集合
        for (T t : dataList) {
            //获取对象的所有属性
            for (Field field : declaredFields) {
                try {
                    String name = field.getName();
                    if (!includeList.contains(name)) continue;
                    field.setAccessible(true);
                    Object o = field.get(t);
                    if (o == null || ObjectUtils.isEmpty(o)) continue;
                    //如果计算总值的map已经包含该字段则直接获取
                    if (map.containsKey(name)) {
                        if (o instanceof Double) {
                            Double o1 = (Double)map.get(name);
                            map.put(name, o1 + (Double) o);
                        } else if (o instanceof Integer) {
                            Integer o1 = (Integer)map.get(name);
                            map.put(name, o1 + (Integer) o);
                        } else {
                            log.info("属性类型不匹配,(支持Double,Integer):{}",field.getName());
                        }
                    } else {
                        if (o instanceof Double) {
                            map.put(name, (Double) o);
                        } else if (o instanceof Integer) {
                            map.put(name, (Integer) o);
                        } else {
                            log.info("属性类型不匹配,(支持Double,Integer):{}",field.getName());
                        }
                    }
                } catch (Exception e) {
                    //出异常则跳过该属性
                    throw new RuntimeException("发生异常跳过属性",e);
                }
            }
        }
        return mapToObject(map, clazz);
    }

    /**
     * map集合转对象,上面方法中专用
     *
     * @param map
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    private static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) throws Exception {
        if (map == null) {
            return null;
        }
        T obj = clazz.getDeclaredConstructor().newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            Method setter = property.getWriteMethod();
            if (setter != null) {
                Object o = map.get(property.getName());
                if (o != null) {
                    try {
                        setter.invoke(obj, o);
                    } catch (IllegalArgumentException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return obj;
    }
    Map<String, List<CurrentStaffCostCpInfoDTO>> curGroupMap = currentStaffGroupList.stream().collect(Collectors.groupingBy((k->empTypeMap.get(k.getEmpType())+"#"+k.getL1()+"#"+k.getL2()+"#"+k.getMonth())));
    // 获取要计算的列
    List<String> curFieldNames = new ArrayList<>();
    curFieldNames.add("monthPeopleNum");//人数
    for (Field field : CurrentStaffCostCpInfoDTO.class.getDeclaredFields()) {
        String name = field.getName();
        if (4 == name.length() && name.startsWith("y")){
            curFieldNames.add(name);
        }
    }
    currentStaffGroupList.clear();
    curGroupMap.forEach((k,v)->{
        try {
            String[] arr = k.split("#");
            CurrentStaffCostCpInfoDTO costCpInfoSum = ReflectUtils.getSumByList(v, CurrentStaffCostCpInfoDTO.class, curFieldNames);
            costCpInfoSum.setEmpType(arr[0]);
            costCpInfoSum.setL1(arr[1]);
            costCpInfoSum.setL2("null".equals(arr[2]) ? null : arr[2]);
            costCpInfoSum.setMonth(Integer.valueOf(arr[3]));
            currentStaffGroupList.add(costCpInfoSum);
        } catch (Exception e) {
            log.warn("现员计算List中各列之和异常",e);
        }
    });

方式二:利用Stream中的Collectors.toMap

    /**
     * 汇总求和Amount1-10
     * @param baseReportAmountList
     * @return
     */
    private static Map<String, BudgetBaseReportDTO> getReportDTOMap(List<BudgetBaseReportDTO> baseReportAmountList) {
        Map<String, BudgetBaseReportDTO> l2GroupAmountMap = baseReportAmountList.stream().collect(Collectors.toMap((k -> k.getL2() + "#" + k.getBaseLineName() + "#" + k.getEmpType() + "#" + k.getCostSubject()), v -> v,
                (v1, v2) -> {
                    BudgetBaseReportDTO dto = new BudgetBaseReportDTO();
                    dto.setEmpType(v1.getEmpType());
                    dto.setL1(v1.getL1());
                    dto.setL2(v1.getL2());
                    dto.setBaseLineName(v1.getBaseLineName());
                    dto.setCostSubject(v1.getCostSubject());
                    dto.setCostSubjectDesc(v1.getCostSubjectDesc());
                    dto.setAmount1(ArithUtil.addTotal(v1.getAmount1(), v2.getAmount1()));
                    dto.setAmount2(ArithUtil.addTotal(v1.getAmount2(), v2.getAmount2()));
                    dto.setAmount3(ArithUtil.addTotal(v1.getAmount3(), v2.getAmount3()));
                    dto.setAmount4(ArithUtil.addTotal(v1.getAmount4(), v2.getAmount4()));
                    dto.setAmount5(ArithUtil.addTotal(v1.getAmount5(), v2.getAmount5()));
                    dto.setAmount6(ArithUtil.addTotal(v1.getAmount6(), v2.getAmount6()));
                    dto.setAmount7(ArithUtil.addTotal(v1.getAmount7(), v2.getAmount7()));
                    dto.setAmount8(ArithUtil.addTotal(v1.getAmount8(), v2.getAmount8()));
                    dto.setAmount9(ArithUtil.addTotal(v1.getAmount9(), v2.getAmount9()));
                    dto.setAmount10(ArithUtil.addTotal(v1.getAmount10(), v2.getAmount10()));
                    dto.setAmount11(ArithUtil.addTotal(v1.getAmount11(), v2.getAmount11()));
                    dto.setAmount12(ArithUtil.addTotal(v1.getAmount12(), v2.getAmount12()));
                    dto.setAmountSum(ArithUtil.addTotal(dto.getAmount1(), dto.getAmount2(), dto.getAmount3(), dto.getAmount4(), dto.getAmount5(), dto.getAmount6()
                            , dto.getAmount7(), dto.getAmount8(), dto.getAmount9(), dto.getAmount10(), dto.getAmount11(), dto.getAmount12()));
                    return dto;
                }));
        return l2GroupAmountMap;
    }

方式三:利用Stream中的Collectors.reducing

    /**
     * 汇总求和Amount1-10
     * @param baseReportAmountList
     * @return
     */
    private static Map<String, BudgetBaseReportDTO> getReportDTOMap2(List<BudgetBaseReportDTO> baseReportAmountList) {
        Map<String, BudgetBaseReportDTO> l2GroupAmountMap = baseReportAmountList.stream()
                .collect(Collectors.groupingBy((k -> k.getL2() + "#" + k.getBaseLineName() + "#" + k.getEmpType() + "#" + k.getCostSubject()),
                        Collectors.reducing(new BudgetBaseReportDTO(),
                                (v1, v2) -> {
                                    BudgetBaseReportDTO dto = new BudgetBaseReportDTO();
                                    dto.setEmpType(v1.getEmpType());
                                    dto.setL1(v1.getL1());
                                    dto.setL2(v1.getL2());
                                    dto.setBaseLineName(v1.getBaseLineName());
                                    dto.setCostSubject(v1.getCostSubject());
                                    dto.setCostSubjectDesc(v1.getCostSubjectDesc());
                                    dto.setAmount1(ArithUtil.addTotal(v1.getAmount1(), v2.getAmount1()));
                                    dto.setAmount2(ArithUtil.addTotal(v1.getAmount2(), v2.getAmount2()));
                                    dto.setAmount3(ArithUtil.addTotal(v1.getAmount3(), v2.getAmount3()));
                                    dto.setAmount4(ArithUtil.addTotal(v1.getAmount4(), v2.getAmount4()));
                                    dto.setAmount5(ArithUtil.addTotal(v1.getAmount5(), v2.getAmount5()));
                                    dto.setAmount6(ArithUtil.addTotal(v1.getAmount6(), v2.getAmount6()));
                                    dto.setAmount7(ArithUtil.addTotal(v1.getAmount7(), v2.getAmount7()));
                                    dto.setAmount8(ArithUtil.addTotal(v1.getAmount8(), v2.getAmount8()));
                                    dto.setAmount9(ArithUtil.addTotal(v1.getAmount9(), v2.getAmount9()));
                                    dto.setAmount10(ArithUtil.addTotal(v1.getAmount10(), v2.getAmount10()));
                                    dto.setAmount11(ArithUtil.addTotal(v1.getAmount11(), v2.getAmount11()));
                                    dto.setAmount12(ArithUtil.addTotal(v1.getAmount12(), v2.getAmount12()));
                                    dto.setAmountSum(ArithUtil.addTotal(dto.getAmount1(), dto.getAmount2(), dto.getAmount3(), dto.getAmount4(), dto.getAmount5(), dto.getAmount6()
                                            , dto.getAmount7(), dto.getAmount8(), dto.getAmount9(), dto.getAmount10(), dto.getAmount11(), dto.getAmount12()));
                                    return dto;
                                })));
        return l2GroupAmountMap;
    }

Collectors.reducing方法的详细说明:

public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op)
public static <T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)

这些方法可以用于将流中的元素按照指定的方式进行归约操作,并返回一个 Optional 或最终的归约结果。
使用 Collectors.reducing 方法时,你需要提供一个二元操作符( BinaryOperator ),它定义了对两个元素进行归约的逻辑。如果你只提供了二元操作符,则归约的结果将被包装在一个 Optional 对象中。如果你还提供了一个起始值(identity),则归约的结果将直接返回,而不是一个 Optional 对象。
以下是一些示例:

// 示例1:使用二元操作符进行归约
Optional<Integer> sumOptional = list.stream()
        .collect(Collectors.reducing((a, b) -> a + b));

// 示例2:使用二元操作符和起始值进行归约
int sum = list.stream()
        .collect(Collectors.reducing(0, (a, b) -> a + b));

// 示例3:使用二元操作符、起始值和转换函数进行归约
String concatenated = list.stream()
        .collect(Collectors.reducing("", String::concat, String::concat));

在示例1中,使用了二元操作符 (a, b) -> a + b 对整数列表进行求和,并将结果包装在一个 Optional 对象中。

在示例2中,使用了二元操作符 (a, b) -> a + b 和起始值 0 对整数列表进行求和,并返回了最终的归约结果。

在示例3中,使用了二元操作符 String::concat 、起始值 "" 和转换函数 String::concat 对字符串列表进行连接操作,并返回了最终的归约结果。

Q.E.D.


如人饮水、冷暖自知