目录
[示例:嵌套 List 对象定义](#示例:嵌套 List 对象定义)
分析
针对多层嵌套 List 对象 (外层 List 包含自定义对象,对象内有浮点数、List <浮点数>、嵌套 List 对象),设计通用反射工具类,无需硬编码适配特定类,支持任意层级嵌套的均值计算:
核心逻辑
- 单个浮点数属性:收集外层 List 所有对象的该属性值 → 过滤空值 → 计算算术均值;
- List <浮点数> 属性:收集所有对象的该列表 → 按索引取所有列表的第 i 位值 → 计算索引均值;
- 嵌套 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;
}