Spring Boot中如何用Stream过滤嵌套List?代码返回空值求排查
问题:Spring Boot Stream处理嵌套列表过滤后返回全null的问题
问题描述
Java新手在Spring Boot中使用Stream处理嵌套品牌列表的年份数据,需求是按品牌名(如Toyota)过滤嵌套品牌列表,保留原年份信息仅含目标品牌。但执行代码后返回全null的YearBrandDTO列表,相关信息如下:
原始数据结构
[ { "id": 1, "year": 2020, "description": "Two thousand twenty", "brands": [ { "id": 1, "name": "Tesla", "description": "This is a car provider from USA", "countryId": 16, "brandNo": "TE001", "categoryId": 23 }, { "id": 2, "name": "Toyota", "description": "This is a car provider from Japan", "countryId": 16, "brandNo": "TE003", "categoryId": 23 } ] }, { "id": 1, "year": 2022, "description": "Two thousand twenty", "brands": [ { "id": 1, "name": "Tesla", "description": "This is a car provider from USA", "countryId": 16, "brandNo": "TE001", "categoryId": 23 }, { "id": 2, "name": "Toyota", "description": "This is a car provider from Japan", "countryId": 16, "brandNo": "TE003", "categoryId": 23 } ] } ]
期望输出
[ { "id": 1, "year": 2020, "description": "Two thousand twenty", "brands": [ { "id": 2, "name": "Toyota", "description": "This is a car provider from Japan", "countryId": 16, "brandNo": "TE003", "categoryId": 23 } ] }, { "id": 1, "year": 2022, "description": "Two thousand twenty", "brands": [ { "id": 2, "name": "Toyota", "description": "This is a car provider from Japan", "countryId": 16, "brandNo": "TE003", "categoryId": 23 } ] } ]
现有代码片段
List<YearBrandDTO> years = yearRepository.findAll().stream() .map(year -> year.getBrands().stream() .filter(brand -> brand.getName().equals("Toyota"))) .map(year -> modelMapper.map(year, YearBrandDTO.class)) .collect(Collectors.toList());
DTO定义
@Data public class YearBrandDTO { private Long id; private Integer year; private String description; private Object brands; }
实际输出
[ { "id": null, "year": null, "description": null, "brands": null } ]
问题原因分析
- 第一个map操作逻辑错误:原代码将
Year对象直接转换为Brand的Stream,后续modelMapper无法将Stream类型映射到YearBrandDTO,导致所有字段为null。 - DTO字段类型不匹配:
YearBrandDTO的brands字段定义为Object,而非具体集合类型(如List<BrandDTO>),映射工具无法正确识别并转换品牌列表。 - 未保留原Year对象属性:处理过程中丢弃了
Year的id、year、description等核心属性,没有将这些信息传递给DTO。
正确实现方案
第一步:修正DTO字段类型
将brands字段改为具体的集合类型,同时补充对应的BrandDTO(如果需要):
@Data public class YearBrandDTO { private Long id; private Integer year; private String description; // 替换为具体的BrandDTO或Brand实体类型 private List<BrandDTO> brands; } // 补充BrandDTO示例 @Data public class BrandDTO { private Long id; private String name; private String description; private Integer countryId; private String brandNo; private Integer categoryId; }
第二步:正确的Stream处理代码
方式一:使用ModelMapper映射
List<YearBrandDTO> years = yearRepository.findAll().stream() .map(year -> { // 过滤当前Year下的目标品牌 List<Brand> filteredBrands = year.getBrands().stream() .filter(brand -> "Toyota".equals(brand.getName())) .collect(Collectors.toList()); // 映射Year到YearBrandDTO,替换过滤后的品牌列表 YearBrandDTO dto = modelMapper.map(year, YearBrandDTO.class); dto.setBrands(modelMapper.map(filteredBrands, new TypeToken<List<BrandDTO>>(){}.getType())); return dto; }) // 可选:过滤掉没有目标品牌的Year记录 .filter(dto -> !dto.getBrands().isEmpty()) .collect(Collectors.toList());
方式二:手动构造DTO(更直观)
List<YearBrandDTO> years = yearRepository.findAll().stream() .map(year -> { // 将过滤后的Brand转换为BrandDTO List<BrandDTO> filteredBrandDTOs = year.getBrands().stream() .filter(brand -> "Toyota".equals(brand.getName())) .map(brand -> modelMapper.map(brand, BrandDTO.class)) .collect(Collectors.toList()); // 手动赋值YearBrandDTO的所有属性 YearBrandDTO dto = new YearBrandDTO(); dto.setId(year.getId()); dto.setYear(year.getYear()); dto.setDescription(year.getDescription()); dto.setBrands(filteredBrandDTOs); return dto; }) .filter(dto -> !dto.getBrands().isEmpty()) .collect(Collectors.toList());
内容的提问来源于stack exchange,提问作者isaac ssemugenyi
相关产品推荐
相关产品推荐

