You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
]

问题原因分析

  1. 第一个map操作逻辑错误:原代码将Year对象直接转换为Brand的Stream,后续modelMapper无法将Stream类型映射到YearBrandDTO,导致所有字段为null。
  2. DTO字段类型不匹配:YearBrandDTO的brands字段定义为Object,而非具体集合类型(如List<BrandDTO>),映射工具无法正确识别并转换品牌列表。
  3. 未保留原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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:54:59