list 对象里面 嵌套list对象,对象的属性 有浮点数,list<浮点数> 对list对象求均值

目录

分析

核心逻辑

数据列子

[示例:嵌套 List 对象定义](#示例:嵌套 List 对象定义)

测试示例(验证多层嵌套均值)

工具类

正常处理方式

AI提问

总结


分析

针对多层嵌套 List 对象 (外层 List 包含自定义对象,对象内有浮点数、List <浮点数>、嵌套 List 对象),设计通用反射工具类,无需硬编码适配特定类,支持任意层级嵌套的均值计算:

核心逻辑

  1. 单个浮点数属性:收集外层 List 所有对象的该属性值 → 过滤空值 → 计算算术均值;
  2. List <浮点数> 属性:收集所有对象的该列表 → 按索引取所有列表的第 i 位值 → 计算索引均值;
  3. 嵌套 List <对象> 属性
    • 找到嵌套 List 的最大长度 → 按索引分组(外层所有对象的第 n 个嵌套对象为一组);
    • 对每组嵌套对象,递归执行「单个浮点数 + List <浮点数>」均值计算;
    • 汇总分组结果为最终嵌套 List。

数据列子

示例:嵌套 List 对象定义

模拟多层嵌套场景(SectionData → List<SallastAcceleration> → List<SubData>):

java 复制代码
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.List;

// 最外层对象
@Data
@Accessors(chain = true)
public class SectionData {
    private Long id;
    private String sectionCode;
    private String lineCode;
    private Integer direction;
    private Float speed; // 单个浮点数
    private Instant dataTime;
    private LocalDateTime arriveTime;
    private LocalDateTime leaveTime;
    private String filePath;
    private List<SallastAcceleration> sallastAcceleration; // 嵌套List对象
}

// 第一层嵌套对象
@Data
@Accessors(chain = true)
public class SallastAcceleration {
    private String sensorCode;
    private Float validValue; // 单个浮点数
    private Float maxValue; // 单个浮点数
    private Long maxValueTime;
    private List<Float> thirdOctavePeaksY; // List<浮点数>
    private List<Integer> spectrumsX; // 非浮点数列表(取第一个值)
    private List<SubData> subDataList; // 第二层嵌套List对象
}

// 第二层嵌套对象
@Data
@Accessors(chain = true)
public class SubData {
    private Double psdValue; // 单个浮点数(Double)
    private List<Double> psdY; // List<Double>
}

测试示例(验证多层嵌套均值)

java 复制代码
package com.jzyg.vims.nvm.link.util.ai.util;

import cn.hutool.core.collection.CollectionUtil;

import java.util.Arrays;
import java.util.List;

public class TestNestedListAvg {
// ===================== 测试代码 =====================
    public static void main(String[] args) {
        // 构建测试数据1:sallastAcceleration 有2个元素
        SallastAcceleration acc1_1 = new SallastAcceleration()
                .setSensorCode("ACC001")
                .setValidValue(1.2f)
                .setMaxValue(3.5f)
                .setThirdOctavePeaksY(Arrays.asList(0.1f, 0.2f))
                .setSpectrumsX(Arrays.asList(100, 200))
                .setSubDataList(Arrays.asList(new SubData().setPsdValue(1.1d).setPsdY(Arrays.asList(0.1d, 0.2d))));

        SallastAcceleration acc1_2 = new SallastAcceleration()
                .setSensorCode("ACC002")
                .setValidValue(2.2f)
                .setMaxValue(4.5f)
                .setThirdOctavePeaksY(Arrays.asList(1.1f, 1.2f))
                .setSpectrumsX(Arrays.asList(300, 400))
                .setSubDataList(Arrays.asList(new SubData().setPsdValue(2.1d).setPsdY(Arrays.asList(1.1d, 1.2d))));

        SectionData data1 = new SectionData()
                .setSectionCode("S001")
                .setDirection(1)
                .setSpeed(60.0f)
                .setSallastAcceleration(Arrays.asList(acc1_1, acc1_2));

        // 构建测试数据2:sallastAcceleration 有2个元素(和数据1长度一致)
        SallastAcceleration acc2_1 = new SallastAcceleration()
                .setSensorCode("ACC001")
                .setValidValue(1.4f)
                .setMaxValue(3.7f)
                .setThirdOctavePeaksY(Arrays.asList(0.2f, 0.3f))
                .setSpectrumsX(Arrays.asList(101, 201))
                .setSubDataList(Arrays.asList(new SubData().setPsdValue(1.3d).setPsdY(Arrays.asList(0.2d, 0.3d))));

        SallastAcceleration acc2_2 = new SallastAcceleration()
                .setSensorCode("ACC002")
                .setValidValue(2.4f)
                .setMaxValue(4.7f)
                .setThirdOctavePeaksY(Arrays.asList(1.2f, 1.3f))
                .setSpectrumsX(Arrays.asList(301, 401))
                .setSubDataList(Arrays.asList(new SubData().setPsdValue(2.3d).setPsdY(Arrays.asList(1.2d, 1.3d))));

        SectionData data2 = new SectionData()
                .setSectionCode("S001")
                .setSpeed(60.0f)
                .setSallastAcceleration(Arrays.asList(acc2_1, acc2_2));

        // 计算均值
        List<SectionData> dataList = Arrays.asList(data1, data2);
        SectionData avgData = NestedListAvgUtil.calculateAvg(dataList, SectionData.class);

        // 验证结果长度(核心:仍为2个元素)
        List<SallastAcceleration> accList = avgData.getSallastAcceleration();
        System.out.println("===== 结果验证 =====");
        System.out.println("sallastAcceleration列表长度:" + accList.size()); // 输出:2

        // 第一个元素均值
        SallastAcceleration avgAcc1 = accList.get(0);
        System.out.println("第一个元素 - validValue均值:" + avgAcc1.getValidValue()); // (1.2+1.4)/2 = 1.3
        System.out.println("第一个元素 - thirdOctavePeaksY[0]:" + avgAcc1.getThirdOctavePeaksY().get(0)); // 0.15

        // 第二个元素均值
        SallastAcceleration avgAcc2 = accList.get(1);
        System.out.println("第二个元素 - validValue均值:" + avgAcc2.getValidValue()); // (2.2+2.4)/2 = 2.3
        System.out.println("第二个元素 - maxValue均值:" + avgAcc2.getMaxValue()); // (4.5+4.7)/2 = 4.6
    }
}

工具类

java 复制代码
package com.jzyg.vims.nvm.link.util.ai.util;
/**
 * 通用嵌套List对象均值计算工具
 * 支持:
 * 1. 单个浮点数(Float/Double)均值
 * 2. List<Float>/List<Double> 按索引均值
 * 3. 嵌套List<任意对象> 按索引分组后递归均值
 */
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class NestedListAvgUtil {

    // 默认小数保留位数
    private static final int DEFAULT_SCALE = 4;

    /**
     * 对外统一入口:计算外层List<对象>的所有属性均值,返回汇总后的单个对象
     * @param list 外层List
     * @param clazz 目标对象类型
     * @return 汇总均值后的单个对象
     */
    public static <T> T calculateAvg(List<T> list, Class<T> clazz) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }

        // ========== 核心修复:处理基础类型/包装类型(无无参构造) ==========
        if (isBasicType(clazz)) {
            return calculateBasicTypeAvg(list, clazz);
        }

        // 初始化自定义对象(仅非基础类型走此逻辑)
        T avgObj = null;
        try {
            avgObj = clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            log.error("初始化自定义对象[{}]失败", clazz.getName(), e);
            return null;
        }

        // 获取所有字段,逐个计算均值并赋值
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                Class<?> fieldType = field.getType();

                // 1. 处理单个浮点数(Float/Double)
                if (isFloatType(fieldType)) {
                    Object avgValue = calculateSingleFloatAvg(list, field);
                    if (avgValue != null) {
                        field.set(avgObj, avgValue);
                    }
                }
                // 2. 处理List<Float>/List<Double>
                else if (isFloatListType(field)) {
                    Object avgList = calculateFloatListIndexAvg(list, field);
                    if (avgList != null) {
                        field.set(avgObj, avgList);
                    }
                }
                // 3. 处理嵌套List<对象>(保持原列表长度)
                else if (isObjectListType(field)) {
                    Object nestedAvgList = calculateNestedObjectListAvg(list, field);
                    if (nestedAvgList != null) {
                        field.set(avgObj, nestedAvgList);
                    }
                }
                // 4. 非数值/列表字段:取第一个非空值
                else {
                    Object firstValue = getFirstNonEmptyValue(list, field);
                    if (firstValue != null) {
                        field.set(avgObj, firstValue);
                    }
                }
            } catch (Exception e) {
                log.error("计算字段[{}]均值失败", field.getName(), e);
            }
        }

        return avgObj;
    }

    // ===================== 基础类型判断(核心新增) =====================
    /**
     * 判断是否为基础类型/包装类型(无无参构造)
     */
    private static <T> boolean isBasicType(Class<T> clazz) {
        return clazz.isPrimitive() // 基础类型(int、float等)
                || clazz == Integer.class
                || clazz == Long.class
                || clazz == Float.class
                || clazz == Double.class
                || clazz == Boolean.class
                || clazz == Byte.class
                || clazz == Short.class
                || clazz == Character.class
                || clazz == String.class;
    }

    /**
     * 计算基础类型列表的均值(核心新增)
     */
    @SuppressWarnings("unchecked")
    private static <T> T calculateBasicTypeAvg(List<T> list, Class<T> clazz) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }

        // 仅处理数值类型的均值,非数值类型返回第一个值
        if (Number.class.isAssignableFrom(clazz) || clazz.isPrimitive()) {
            List<Number> values = list.stream()
                    .filter(Objects::nonNull)
                    .map(obj -> (Number) obj)
                    .collect(Collectors.toList());

            if (CollectionUtil.isEmpty(values)) {
                return null;
            }

            double sum = values.stream().mapToDouble(Number::doubleValue).sum();
            double avg = sum / values.size();
            BigDecimal avgBig = NumberUtil.round(avg, DEFAULT_SCALE);

            // 转换为对应基础类型/包装类型
            if (clazz == int.class || clazz == Integer.class) {
                return (T) Integer.valueOf(avgBig.intValue());
            } else if (clazz == long.class || clazz == Long.class) {
                return (T) Long.valueOf(avgBig.longValue());
            } else if (clazz == float.class || clazz == Float.class) {
                return (T) Float.valueOf(avgBig.floatValue());
            } else if (clazz == double.class || clazz == Double.class) {
                return (T) Double.valueOf(avgBig.doubleValue());
            }
        }

        // 非数值基础类型(String/Boolean等),返回第一个非空值
        return list.stream().filter(Objects::nonNull).findFirst().orElse(null);
    }

    // ===================== 原有逻辑:基础类型判断(浮点数) =====================
    private static boolean isFloatType(Class<?> clazz) {
        return clazz == Float.class || clazz == float.class || clazz == Double.class || clazz == double.class;
    }

    private static boolean isFloatListType(Field field) {
        if (!List.class.isAssignableFrom(field.getType())) {
            return false;
        }
        Class<?> genericType = getFieldGenericType(field);
        return genericType != null && isFloatType(genericType);
    }

    private static boolean isObjectListType(Field field) {
        if (!List.class.isAssignableFrom(field.getType())) {
            return false;
        }
        return !isFloatListType(field);
    }

    // ===================== 原有逻辑:单个浮点数均值 =====================
    private static <T> Object calculateSingleFloatAvg(List<T> list, Field field) {
        List<Number> values = list.stream()
                .map(obj -> {
                    try {
                        return (Number) field.get(obj);
                    } catch (Exception e) {
                        log.error("获取字段[{}]值失败", field.getName(), e);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (CollectionUtil.isEmpty(values)) {
            log.warn("字段[{}]无有效数值数据", field.getName());
            return null;
        }

        double sum = values.stream().mapToDouble(Number::doubleValue).sum();
        double avg = sum / values.size();
        BigDecimal avgBig = NumberUtil.round(avg, DEFAULT_SCALE);

        if (field.getType() == Float.class || field.getType() == float.class) {
            return avgBig.floatValue();
        } else {
            return avgBig.doubleValue();
        }
    }

    // ===================== 原有逻辑:List<浮点数> 按索引均值 =====================
    @SuppressWarnings({"rawtypes", "unchecked"})
    private static <T> Object calculateFloatListIndexAvg(List<T> list, Field field) {
        // 1. 收集所有非空列表
        List<List<Number>> allLists = new ArrayList<>();
        for (T obj : list) {
            try {
                List<?> fieldValue = (List<?>) field.get(obj);
                if (CollectionUtil.isEmpty(fieldValue)) {
                    continue;
                }
                List<Number> numberList = new ArrayList<>();
                for (Object val : fieldValue) {
                    if (val instanceof Number) {
                        numberList.add((Number) val);
                    }
                }
                if (!numberList.isEmpty()) {
                    allLists.add(numberList);
                }
            } catch (Exception e) {
                log.error("获取字段[{}]列表值失败", field.getName(), e);
            }
        }

        if (CollectionUtil.isEmpty(allLists)) {
            log.warn("字段[{}]无有效列表数据", field.getName());
            return new ArrayList<>();
        }

        // 2. 取所有列表的最小长度(保持原列表长度)
        int minSize = allLists.stream().mapToInt(List::size).min().orElse(0);
        if (minSize == 0) {
            log.warn("字段[{}]列表最小长度为0", field.getName());
            return new ArrayList<>();
        }

        // 3. 按索引计算均值
        List<Object> avgList = new ArrayList<>();
        Class<?> genericType = getFieldGenericType(field);

        for (int i = 0; i < minSize; i++) {
            final int index = i;
            List<Number> indexValues = allLists.stream()
                    .filter(l -> index < l.size())
                    .map(l -> l.get(index))
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            if (CollectionUtil.isEmpty(indexValues)) {
                avgList.add(genericType == Float.class ? 0.0f : 0.0d);
                continue;
            }

            double avg = indexValues.stream().mapToDouble(Number::doubleValue).average().orElse(0);
            BigDecimal avgBig = NumberUtil.round(avg, DEFAULT_SCALE);
            if (genericType == Float.class || genericType == float.class) {
                avgList.add(avgBig.floatValue());
            } else {
                avgList.add(avgBig.doubleValue());
            }
        }

        return avgList;
    }

    // ===================== 原有逻辑:嵌套List<对象> 均值(保持原长度) =====================
    @SuppressWarnings({"rawtypes", "unchecked"})
    private static <T> Object calculateNestedObjectListAvg(List<T> list, Field field) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1. 获取嵌套对象的类型
        Class<?> nestedClass = getFieldGenericType(field);
        if (nestedClass == null) {
            log.warn("字段[{}]无法获取嵌套对象泛型类型", field.getName());
            return new ArrayList<>();
        }

        // 2. 取所有外层对象的嵌套List的最小长度(关键:保持原列表长度)
        int minSize = Integer.MAX_VALUE;
        for (T obj : list) {
            try {
                List nestedList = (List) field.get(obj);
                if (CollectionUtil.isNotEmpty(nestedList)) {
                    minSize = Math.min(minSize, nestedList.size()); // 改为取最小长度
                } else {
                    minSize = 0; // 有空列表则长度为0
                }
            } catch (Exception e) {
                log.error("获取嵌套List长度失败", e);
                minSize = 0;
            }
        }

        if (minSize == 0 || minSize == Integer.MAX_VALUE) {
            log.warn("字段[{}]嵌套List无有效数据", field.getName());
            return new ArrayList<>();
        }

        // 3. 按索引分组计算均值(仅遍历到最小长度)
        List<Object> avgNestedList = new ArrayList<>();
        for (int i = 0; i < minSize; i++) { // 遍历到最小长度,保证结果长度和原列表一致
            // 收集所有外层对象的第i个嵌套对象
            List<Object> nestedGroup = new ArrayList<>();
            for (T obj : list) {
                try {
                    List nestedList = (List) field.get(obj);
                    if (CollectionUtil.isNotEmpty(nestedList) && i < nestedList.size()) {
                        Object nestedObj = nestedList.get(i);
                        if (nestedObj != null) {
                            nestedGroup.add(nestedObj);
                        }
                    }
                } catch (Exception e) {
                    log.error("收集嵌套对象分组失败", e);
                }
            }

            // 递归计算该分组的均值(此时会先判断是否是基础类型)
            Object avgNestedObj = CollectionUtil.isEmpty(nestedGroup)
                    ? (isBasicType(nestedClass) ? null : nestedClass.getDeclaredConstructor().newInstance())
                    : calculateAvg((List) nestedGroup, nestedClass);
            avgNestedList.add(avgNestedObj);
        }

        return avgNestedList;
    }

    // ===================== 原有逻辑:辅助工具方法 =====================
    private static Class<?> getFieldGenericType(Field field) {
        try {
            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType parameterizedType) {
                Type actualType = parameterizedType.getActualTypeArguments()[0];
                if (actualType instanceof Class<?>) {
                    return (Class<?>) actualType;
                }
            }
        } catch (Exception e) {
            log.error("获取字段[{}]的泛型类型失败", field.getName(), e);
        }
        return null;
    }

    private static <T> Object getFirstNonEmptyValue(List<T> list, Field field) {
        for (T obj : list) {
            try {
                Object value = field.get(obj);
                if (value != null) {
                    return value;
                }
            } catch (Exception e) {
                log.error("获取第一个非空值失败", e);
            }
        }
        return null;
    }
}

正常处理方式

java 复制代码
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * SectionData 列表均值汇总工具类
 * 适配 SallastAcceleration 为 List 类型的场景(按索引分组计算均值)
 */
@Slf4j
public class SectionDataAvgUtil {

    /**
     * 对 List<SectionData> 所有属性求均值,返回汇总后的 SectionData 对象
     * @param dataList 原始数据列表
     * @return 汇总后的 SectionData(含下层 SallastAcceleration 按索引分组汇总)
     */
    public static SectionData calculateAvg(List<SectionData> dataList) {
        if (CollectionUtil.isEmpty(dataList)) {
            return null;
        }

        // 1. 初始化汇总对象
        SectionData avgSectionData = new SectionData();

        // 2. 处理顶层字符串/时间类型字段(取第一个非空值)
        fillBaseNonNumericFields(dataList, avgSectionData);

        // 3. 处理顶层数值单值字段(求均值)
        fillTopLevelNumericAvg(dataList, avgSectionData);

        // 4. 处理下层 SallastAcceleration 列表(按索引分组汇总)
        List<SallastAcceleration> avgSallastAccList = calculateSallastAccelerationAvg(dataList);
        avgSectionData.setSallastAcceleration(avgSallastAccList);

        return avgSectionData;
    }

    // ===================== 顶层字段处理 =====================
    /**
     * 填充顶层非数值型字段(字符串/时间,取第一个非空值)
     */
    private static void fillBaseNonNumericFields(List<SectionData> dataList, SectionData avgData) {
        // 遍历列表,找到第一个非空的基础字段值
        for (SectionData data : dataList) {
            // 断面编号
            if (avgData.getSectionCode() == null && data.getSectionCode() != null) {
                avgData.setSectionCode(data.getSectionCode());
            }
            // 地铁线路编码
            if (avgData.getLineCode() == null && data.getLineCode() != null) {
                avgData.setLineCode(data.getLineCode());
            }
            // 车次/车号
            if (avgData.getTrainNumber() == null && data.getTrainNumber() != null) {
                avgData.setTrainNumber(data.getTrainNumber());
            }
            if (avgData.getTrainCode() == null && data.getTrainCode() != null) {
                avgData.setTrainCode(data.getTrainCode());
            }
            // 文件路径(取第一个非空)
            if (avgData.getFilePath() == null && data.getFilePath() != null) {
                avgData.setFilePath(data.getFilePath());
            }
            // 时间字段(取第一个非空)
            if (avgData.getDataTime() == null && data.getDataTime() != null) {
                avgData.setDataTime(data.getDataTime());
            }
            if (avgData.getArriveTime() == null && data.getArriveTime() != null) {
                avgData.setArriveTime(data.getArriveTime());
            }
            if (avgData.getLeaveTime() == null && data.getLeaveTime() != null) {
                avgData.setLeaveTime(data.getLeaveTime());
            }

            // 所有基础字段已填充,提前退出
            if (avgData.getSectionCode() != null && avgData.getLineCode() != null
                    && avgData.getTrainNumber() != null && avgData.getTrainCode() != null) {
                break;
            }
        }
    }

    /**
     * 计算顶层数值单值字段的均值(direction、speed)
     */
    private static void fillTopLevelNumericAvg(List<SectionData> dataList, SectionData avgData) {
        // ---------------- 1. direction(Integer)均值 ----------------
        List<Integer> directionList = dataList.stream()
                .map(SectionData::getDirection)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(directionList)) {
            double directionAvg = directionList.stream().mapToInt(Integer::intValue).average().orElse(0);
            // 行别是枚举值(0/1),均值取四舍五入(或根据业务保留小数)
            avgData.setDirection((int) Math.round(directionAvg));
        }

        // ---------------- 2. speed(Float)均值 ----------------
        List<Float> speedList = dataList.stream()
                .map(SectionData::getSpeed)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(speedList)) {
            float speedAvg = (float) speedList.stream().mapToDouble(Float::doubleValue).average().orElse(0);
            // 保留2位小数(按需调整)
            avgData.setSpeed(NumberUtil.round(speedAvg, 2).floatValue());
        }
    }

    // ===================== 下层 SallastAcceleration 处理(核心调整) =====================
    /**
     * 按索引分组汇总所有 SectionData 中的 SallastAcceleration 列表
     * 逻辑:
     * 1. 找到所有 SectionData 的 SallastAcceleration 列表的最大长度 → 汇总后列表的长度
     * 2. 对每个索引位置 i,收集所有 SectionData 的第 i 个 SallastAcceleration → 分组
     * 3. 对每组计算均值 → 汇总后列表的第 i 个元素
     */
    private static List<SallastAcceleration> calculateSallastAccelerationAvg(List<SectionData> dataList) {
        // 1. 确定所有 SallastAcceleration 列表的最大长度
        int maxAccListSize = 0;
        for (SectionData data : dataList) {
            if (CollectionUtil.isNotEmpty(data.getSallastAcceleration())) {
                maxAccListSize = Math.max(maxAccListSize, data.getSallastAcceleration().size());
            }
        }
        if (maxAccListSize == 0) {
            return new ArrayList<>();
        }

        // 2. 按索引分组计算均值
        List<SallastAcceleration> avgAccList = new ArrayList<>();
        for (int i = 0; i < maxAccListSize; i++) {
            // 收集所有 SectionData 的第 i 个 SallastAcceleration(非空)
            List<SallastAcceleration> accGroup = new ArrayList<>();
            for (SectionData data : dataList) {
                List<SallastAcceleration> accList = data.getSallastAcceleration();
                if (CollectionUtil.isNotEmpty(accList) && i < accList.size()) {
                    SallastAcceleration acc = accList.get(i);
                    if (acc != null) {
                        accGroup.add(acc);
                    }
                }
            }

            // 计算该索引分组的均值(无数据则初始化空对象)
            SallastAcceleration avgAcc = CollectionUtil.isEmpty(accGroup) 
                    ? new SallastAcceleration() 
                    : calculateSingleAccGroupAvg(accGroup);
            avgAccList.add(avgAcc);
        }

        return avgAccList;
    }

    /**
     * 计算单个索引分组内的 SallastAcceleration 均值(单值+列表)
     */
    private static SallastAcceleration calculateSingleAccGroupAvg(List<SallastAcceleration> accGroup) {
        if (CollectionUtil.isEmpty(accGroup)) {
            return new SallastAcceleration();
        }

        // 1. 初始化汇总对象
        SallastAcceleration avgAcc = new SallastAcceleration();

        // 2. 填充字符串字段(sensorCode 取第一个非空)
        for (SallastAcceleration acc : accGroup) {
            if (avgAcc.getSensorCode() == null && acc.getSensorCode() != null) {
                avgAcc.setSensorCode(acc.getSensorCode());
                break;
            }
        }

        // 3. 计算单值数值字段均值
        fillSingleAccGroupSingleValueAvg(accGroup, avgAcc);

        // 4. 计算列表字段按索引均值
        fillSingleAccGroupListIndexAvg(accGroup, avgAcc);

        return avgAcc;
    }

    /**
     * 计算单个分组内 SallastAcceleration 单值数值字段的均值
     */
    private static void fillSingleAccGroupSingleValueAvg(List<SallastAcceleration> accGroup, SallastAcceleration avgAcc) {
        // ---------------- 1. validValue(Float) ----------------
        List<Float> validValueList = accGroup.stream()
                .map(SallastAcceleration::getValidValue)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(validValueList)) {
            float validValueAvg = (float) validValueList.stream().mapToDouble(Float::doubleValue).average().orElse(0);
            avgAcc.setValidValue(NumberUtil.round(validValueAvg, 4).floatValue());
        }

        // ---------------- 2. maxValue(Float) ----------------
        List<Float> maxValueList = accGroup.stream()
                .map(SallastAcceleration::getMaxValue)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(maxValueList)) {
            float maxValueAvg = (float) maxValueList.stream().mapToDouble(Float::doubleValue).average().orElse(0);
            avgAcc.setMaxValue(NumberUtil.round(maxValueAvg, 4).floatValue());
        }

        // ---------------- 3. doubleIntegral(Float) ----------------
        List<Float> doubleIntegralList = accGroup.stream()
                .map(SallastAcceleration::getDoubleIntegral)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(doubleIntegralList)) {
            float doubleIntegralAvg = (float) doubleIntegralList.stream().mapToDouble(Float::doubleValue).average().orElse(0);
            avgAcc.setDoubleIntegral(NumberUtil.round(doubleIntegralAvg, 4).floatValue());
        }

        // ---------------- 4. maxValueTime(Long) ----------------
        List<Long> maxValueTimeList = accGroup.stream()
                .map(SallastAcceleration::getMaxValueTime)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(maxValueTimeList)) {
            long maxValueTimeAvg = (long) maxValueTimeList.stream().mapToLong(Long::longValue).average().orElse(0);
            avgAcc.setMaxValueTime(maxValueTimeAvg);
        }
    }

    /**
     * 计算单个分组内 SallastAcceleration 列表字段的按索引均值
     */
    private static void fillSingleAccGroupListIndexAvg(List<SallastAcceleration> accGroup, SallastAcceleration avgAcc) {
        // ---------------- 1. Float 类型列表 ----------------
        avgAcc.setThirdOctavePeaksY(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getThirdOctavePeaksY));
        avgAcc.setThirdOctavePeaksX(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getThirdOctavePeaksX));
        avgAcc.setThirdOctaveLinearsY(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getThirdOctaveLinearsY));
        avgAcc.setThirdOctaveLinearsX(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getThirdOctaveLinearsX));
        avgAcc.setSpectrumsY(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getSpectrumsY));
        avgAcc.setPsdY(calculateFloatListIndexAvg(accGroup, SallastAcceleration::getPsdY));

        // ---------------- 2. Integer 类型列表 ----------------
        avgAcc.setSpectrumsX(calculateIntegerListIndexAvg(accGroup, SallastAcceleration::getSpectrumsX));
        avgAcc.setPsdX(calculateIntegerListIndexAvg(accGroup, SallastAcceleration::getPsdX));
    }

    // ===================== 通用列表均值计算方法 =====================
    /**
     * 通用方法:计算 Float 类型列表的按索引均值
     * @param accGroup 单个索引分组的 SallastAcceleration 列表
     * @param getter 列表字段的getter方法
     * @return 按索引均值后的列表
     */
    private static List<Float> calculateFloatListIndexAvg(List<SallastAcceleration> accGroup,
                                                          Function<SallastAcceleration, List<Float>> getter) {
        // 1. 收集所有非空列表
        List<List<Float>> allLists = accGroup.stream()
                .map(getter)
                .filter(Objects::nonNull)
                .filter(list -> !list.isEmpty())
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allLists)) {
            return new ArrayList<>();
        }

        // 2. 取所有列表的最小长度(避免索引越界)
        int minSize = allLists.stream().mapToInt(List::size).min().orElse(0);
        if (minSize == 0) {
            return new ArrayList<>();
        }

        // 3. 按索引计算均值
        List<Float> avgList = new ArrayList<>();
        for (int i = 0; i < minSize; i++) {
            // 收集所有列表的第i位元素
            List<Float> indexValues = new ArrayList<>();
            for (List<Float> list : allLists) {
                if (i < list.size()) {
                    Float val = list.get(i);
                    if (val != null) {
                        indexValues.add(val);
                    }
                }
            }
            // 计算均值(保留4位小数)
            if (CollectionUtil.isNotEmpty(indexValues)) {
                double avg = indexValues.stream().mapToDouble(Float::doubleValue).average().orElse(0);
                avgList.add(NumberUtil.round(avg, 4).floatValue());
            } else {
                avgList.add(0.0f); // 无数据时填0
            }
        }
        return avgList;
    }

    /**
     * 通用方法:计算 Integer 类型列表的按索引均值
     * @param accGroup 单个索引分组的 SallastAcceleration 列表
     * @param getter 列表字段的getter方法
     * @return 按索引均值后的列表(均值取整)
     */
    private static List<Integer> calculateIntegerListIndexAvg(List<SallastAcceleration> accGroup,
                                                              Function<SallastAcceleration, List<Integer>> getter) {
        // 1. 收集所有非空列表
        List<List<Integer>> allLists = accGroup.stream()
                .map(getter)
                .filter(Objects::nonNull)
                .filter(list -> !list.isEmpty())
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allLists)) {
            return new ArrayList<>();
        }

        // 2. 取所有列表的最小长度
        int minSize = allLists.stream().mapToInt(List::size).min().orElse(0);
        if (minSize == 0) {
            return new ArrayList<>();
        }

        // 3. 按索引计算均值(四舍五入取整)
        List<Integer> avgList = new ArrayList<>();
        for (int i = 0; i < minSize; i++) {
            List<Integer> indexValues = new ArrayList<>();
            for (List<Integer> list : allLists) {
                if (i < list.size()) {
                    Integer val = list.get(i);
                    if (val != null) {
                        indexValues.add(val);
                    }
                }
            }
            if (CollectionUtil.isNotEmpty(indexValues)) {
                double avg = indexValues.stream().mapToInt(Integer::intValue).average().orElse(0);
                avgList.add((int) Math.round(avg));
            } else {
                avgList.add(0); // 无数据时填0
            }
        }
        return avgList;
    }
}

测试示例(模拟多列表场景)

java 复制代码
public class TestSectionDataAvg {
    public static void main(String[] args) {
        // 模拟数据1:sallastAcceleration 有2个元素
        SallastAcceleration acc1_1 = new SallastAcceleration()
                .setSensorCode("ACC001")
                .setValidValue(1.2f)
                .setMaxValue(3.5f)
                .setThirdOctavePeaksY(Arrays.asList(0.1f, 0.2f))
                .setSpectrumsX(Arrays.asList(100, 200));

        SallastAcceleration acc1_2 = new SallastAcceleration()
                .setSensorCode("ACC002")
                .setValidValue(2.2f)
                .setMaxValue(4.5f)
                .setThirdOctavePeaksY(Arrays.asList(1.1f, 1.2f))
                .setSpectrumsX(Arrays.asList(300, 400));

        SectionData data1 = new SectionData()
                .setSectionCode("S001")
                .setDirection(1)
                .setSpeed(60.0f)
                .setSallastAcceleration(Arrays.asList(acc1_1, acc1_2));

        // 模拟数据2:sallastAcceleration 有3个元素(比数据1多1个)
        SallastAcceleration acc2_1 = new SallastAcceleration()
                .setSensorCode("ACC001")
                .setValidValue(1.4f)
                .setMaxValue(3.7f)
                .setThirdOctavePeaksY(Arrays.asList(0.2f, 0.3f))
                .setSpectrumsX(Arrays.asList(101, 201));

        SallastAcceleration acc2_2 = new SallastAcceleration()
                .setSensorCode("ACC002")
                .setValidValue(2.4f)
                .setMaxValue(4.7f)
                .setThirdOctavePeaksY(Arrays.asList(1.2f, 1.3f))
                .setSpectrumsX(Arrays.asList(301, 401));

        SallastAcceleration acc2_3 = new SallastAcceleration()
                .setSensorCode("ACC003")
                .setValidValue(3.4f)
                .setMaxValue(5.7f)
                .setThirdOctavePeaksY(Arrays.asList(2.2f, 2.3f))
                .setSpectrumsX(Arrays.asList(501, 601));

        SectionData data2 = new SectionData()
                .setSectionCode("S001")
                .setDirection(0)
                .setSpeed(60.0f)
                .setSallastAcceleration(Arrays.asList(acc2_1, acc2_2, acc2_3));

        // 计算均值
        List<SectionData> dataList = Arrays.asList(data1, data2);
        SectionData avgData = SectionDataAvgUtil.calculateAvg(dataList);

        // 打印结果
        System.out.println("汇总后sallastAcceleration列表长度:" + avgData.getSallastAcceleration().size()); // 3(最大长度)

        // 索引0的汇总结果(acc1_1 + acc2_1 的均值)
        SallastAcceleration avgAcc0 = avgData.getSallastAcceleration().get(0);
        System.out.println("索引0 - validValue均值:" + avgAcc0.getValidValue()); // (1.2+1.4)/2 = 1.3
        System.out.println("索引0 - thirdOctavePeaksY第0位:" + avgAcc0.getThirdOctavePeaksY().get(0)); // (0.1+0.2)/2 = 0.15
        System.out.println("索引0 - spectrumsX第1位:" + avgAcc0.getSpectrumsX().get(1)); // (200+201)/2 = 200(取整)

        // 索引1的汇总结果(acc1_2 + acc2_2 的均值)
        SallastAcceleration avgAcc1 = avgData.getSallastAcceleration().get(1);
        System.out.println("索引1 - validValue均值:" + avgAcc1.getValidValue()); // (2.2+2.4)/2 = 2.3
        System.out.println("索引1 - maxValue均值:" + avgAcc1.getMaxValue()); // (4.5+4.7)/2 = 4.6

        // 索引2的汇总结果(仅acc2_3,无其他数据 → 直接取acc2_3的值)
        SallastAcceleration avgAcc2 = avgData.getSallastAcceleration().get(2);
        System.out.println("索引2 - validValue均值:" + avgAcc2.getValidValue()); // 3.4(仅acc2_3)
        System.out.println("索引2 - spectrumsX第0位:" + avgAcc2.getSpectrumsX().get(0)); // 501
    }
}

AI提问

java 复制代码
	
	AI提问:
	@Data
	@Accessors(chain = true)
	@Schema(description = "断面数据")
	@TableName("t_section_data")
	public class SectionData {

    /**
     * ID
     */
    @Schema(description = "ID")
    private Long id;
    /**
     * 断面编号
     */
    @Schema(name = "断面编号")
    private String sectionCode;

    @Schema(name = "地铁线路编码")
    private String lineCode;

    @Schema(name = "行别:1-上行 0-下行")
    private Integer direction;

    @Schema(name = "车次")
    private String trainNumber;

    @Schema(name = "车号")
    private String trainCode;

    @Schema(name = "速度")
    private Float speed;

    /**
     * 数据时间戳
     */
    @Schema(name = "数据时间戳")
    private Instant dataTime;
    /**
     * 列车通过初始时间
     */
    @Schema(name = "列车通过初始时间")
    private LocalDateTime arriveTime;
    /**
     * 列车通过截止时间
     */
    @Schema(name = "列车通过截止时间")
    private LocalDateTime leaveTime;
    /**
     * 文件路径
     */
    @Schema(name = "文件路径")
    private String filePath;

	List<SallastAcceleration> sallastAcceleration;
	}


	/**
	 * 道床加速度
	 */
	@Data
	@Accessors(chain = true)
	public class SallastAcceleration {
    //传感器
    private String sensorCode;
    // 1/3倍频程(峰值保持)(29*2)
    private List<Float> thirdOctavePeaksY;
    // 1/3倍频程(峰值保持)(29*2)
    private List<Float> thirdOctavePeaksX;
    // 1/3倍频程(线性平均)(29*2)
    private List<Float> thirdOctaveLinearsY;
    // 1/3倍频程(线性平均)(29*2)
    private List<Float> thirdOctaveLinearsX;
    // 频谱 (200*2)
    private List<Float> spectrumsY;
    // 频谱 (200*2)
    private List<Integer> spectrumsX;
    // 时域有效值(截取后数据)
    private Float validValue;
    // 时域最大值时刻
    private Long maxValueTime;
    // 时域最大值
    private Float maxValue;
    // 二次积分(位移)
    private Float doubleIntegral;
    // PSD
    private List<Float> psdY;
    // PSD
    private List<Integer> psdX;
	}  

List<SectionData> 列表有多条记录,对List<SectionData>中每个属性求均值,传递下层对象

1、需要对 List<SectionData> 列表中所有记录的顶层单值属性(如 speed、direction)计算均值,同时将均值计算逻辑传递到下层 SallastAcceleration 对象(包括单值属性均值、列表属性按索引均值),最终返回一个汇总后的 SectionData 对象。

2、list 对象里面 嵌套list对象,对象的属性 有浮点数,list<浮点数> 对list对象求均值

总结

单值求均值思路:先收集数据 单值 直接收集求均值列,然后求平均值,保留四位小数

java 复制代码
		   List<Float> validValueList = accGroup.stream()
                .map(SallastAccelerationAi::getValidValue)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(validValueList)) {
            float validValueAvg = (float) validValueList.stream().mapToDouble(Float::doubleValue).average().orElse(0);
            avgAcc.setValidValue(NumberUtil.round(validValueAvg, 4).floatValue());
        }
	

列表求均值思路:先收集数据,然后求平均值,保留四位小数

java 复制代码
		
	 /**
     * 通用方法:计算 Float 类型列表的按索引均值
     * @param accGroup 单个索引分组的 SallastAccelerationAi 列表
     * @param getter 列表字段的getter方法
     * @return 按索引均值后的列表
     */
    private static List<Float> calculateFloatListIndexAvg(List<SallastAccelerationAi> accGroup,
                                                          Function<SallastAccelerationAi, List<Float>> getter) {
        // 1. 收集所有非空列表
        List<List<Float>> allLists = accGroup.stream()
                .map(getter)
                .filter(Objects::nonNull)
                .filter(list -> !list.isEmpty())
                .collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allLists)) {
            return new ArrayList<>();
        }

        // 2. 取所有列表的最小长度(避免索引越界)
        int minSize = allLists.stream().mapToInt(List::size).min().orElse(0);
        if (minSize == 0) {
            return new ArrayList<>();
        }

        // 3. 按索引计算均值
        List<Float> avgList = new ArrayList<>();
        for (int i = 0; i < minSize; i++) {
            // 收集所有列表的第i位元素
            List<Float> indexValues = new ArrayList<>();
            for (List<Float> list : allLists) {
                if (i < list.size()) {
                    Float val = list.get(i);
                    if (val != null) {
                        indexValues.add(val);
                    }
                }
            }
            // 计算均值(保留4位小数)
            if (CollectionUtil.isNotEmpty(indexValues)) {
                double avg = indexValues.stream().mapToDouble(Float::doubleValue).average().orElse(0);
                avgList.add(NumberUtil.round(avg, 4).floatValue());
            } else {
                avgList.add(0.0f); // 无数据时填0
            }
        }
        return avgList;
    }
相关推荐
wanghowie2 小时前
01.09 Java基础篇|算法与数据结构实战
java·数据结构·算法
郝学胜-神的一滴2 小时前
GLSL语法详解:从入门到实战
c++·算法·图形渲染
qq_463408422 小时前
React Native跨平台技术在开源鸿蒙中使用内置的`fetch` API或者第三方库如`axHarmony`来处理网络通信HTTP请求
javascript·算法·react native·react.js·http·开源·harmonyos
蒙奇D索大2 小时前
【数据结构】考研408 | 散列查找性能剖析:装填因子、冲突策略与优化全攻略
数据结构·考研·算法·哈希算法·改行学it
88号技师2 小时前
2025年10月一区SCI-中心碰撞优化算法Centered Collision Optimizer-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法
zore_c2 小时前
【数据结构】堆——超详解!!!(包含堆的实现)
c语言·开发语言·数据结构·经验分享·笔记·算法·链表
月明长歌3 小时前
【码道初阶】【LeetCode387】如何高效找到字符串中第一个不重复的字符?
java·开发语言·数据结构·算法·leetcode·哈希算法
罗湖老棍子3 小时前
C++ 自定义排序与优先队列运算符重载
数据结构·c++·算法··优先队列·运算符重载
毅炼3 小时前
hot100打卡——day01
算法