【问题标题】:group objects based on matching properties into list in java 8 or java 11基于匹配属性将对象分组到 java 8 或 java 11 中的列表中
【发布时间】:2022-01-15 07:07:35
【问题描述】:

我的课程类似于:

class Response {
    Source source;
    Target target;
}

class Source {
    Long sourceId;
    String sourceName;
}

class Target {
    Long regionId;
    String regionName;
    Long countryId;
    String countryName;
}

在响应中,source(sourceId,sourceName) 对于不同的 Target 对象可能是相同的。 同样,我还想根据regionIdregionNameTarget 对象中进行分组。 对于regionIdregionName 的组合,我们可以在目标对象中包含Listcountries

我在数据库中有这 6 个属性 sourceId, sourceName, targetId, targetName,countryId,countryName 的条目。我可以在多行上使用相同的 sourceId, sourceName,但 target 总是不同的。

我想将所有目标对象分组到源相同的列表中。

我有响应对象列表,我正在尝试对其执行 stream() 操作,例如:

List<Response> responses; // set up the input list

List<FinalResponse> finalResponseLst = responses.stream()
    .collect(Collectors.groupingBy(
        Response::getSource,
        Collectors.mapping(Response::getTarget, Collectors.toList())
    )) 
    .entrySet()
    .stream() 
    .map(e -> new FinalResponse(e.getKey(), e.getValue()))
    .collect(Collectors.toList());

这给了我Source 和它们各自的Target 对象。但是如何根据地区对目标对象内的国家进行分组呢?如何为单个Target 对象创建具有相同地区的国家/地区列表。 所以我的最终输出 JSON 看起来像:

Response": { 
    "Source":       {  
        "sourceId":       "100",   
        "sourceName":      "source1",    
    },
    "Targets": [
    {
    "TargetRegion":{//grouping target objects with same regions
        "regionId":       "100",   
        "regionName":      "APAC",          
    }, 
    "TargetCountries":[{
        "countryId":       "1",   
        "countryName":      "USA",   
    },
    {
        "targetId":       "2",   
        "targetName":      "China",   
    },
    {
        "targetId":       "3",   
        "targetName":     "Brazil"   
    }
    ]
    },
    {
    "TargetRegion":{//grouping target objects with same regions
       
        "regionId":       "200",   
        "regionName":      "ASPAC",         
    }, 
    "TargetCountries":[{
        "countryId":       "11",   
        "countryName":      "Japan",   
    },
    {
        "targetId":       "22",   
        "targetName":      "Combodia",   
    },
    {
        "targetId":       "33",   
        "targetName":     "Thailand"   
    }
    ]
    }

    ]
}

【问题讨论】:

    标签: java collections java-8 java-stream java-11


    【解决方案1】:

    您应该只添加额外的分组并确保存在适当的 POJO 类:

    • POJO 类:
    @Data
    @AllArgsConstructor
    class Region {
        Long regionId;
        String regionName;
    }
    
    @Data
    @AllArgsConstructor
    class Country {
        Long countryId;
        String countryName;
    }
    
    @Data
    @AllArgsConstructor
    class RegionCountries {
        private Region targetRegion;
        private List<Country> targetCountries;
    }
    
    @Data
    @AllArgsConstructor
    class FinalResponse {
        Source source;
        List<RegionCountries> targets;
    }
    

    那么收集可以如下实现。
    如果输入列表responses 已排序并且需要维护此排序,则应将LinkedHashMap::new供应商应用于所有地图:

    List<FinalResponse> f = responses.stream()
            .collect(Collectors.groupingBy(
                    Response::getSource,
                    LinkedHashMap::new, // if pre-sorted by source
                    Collectors.groupingBy(
                            r -> new Region(
                                r.getTarget().getRegionId(),
                                r.getTarget().getRegionName()
                            ),
                            LinkedHashMap::new, // if pre-sorted by region
                            Collectors.mapping(r -> new Country(
                                    r.getTarget().getCountryId(), 
                                    r.getTarget().getCountryName()
                                ),
                                Collectors.toList()
                            )
                    )
            )) // Map<Source, Map<Region, List<Country>>>
            .entrySet()
            .stream()
            .map(e -> new FinalResponse(
                    e.getKey(),
                    e.getValue().entrySet()
                     .stream()
                     .map(re -> new RegionCountries(re.getKey(), re.getValue()))
                     .collect(Collectors.toList()) // List<RegionCountries>
            ))
            .collect(Collectors.toList());
    

    如果输入未排序或需要更改现有排序,则在收集地图的中间地图时,可能会在收集结果列表之前根据需要对其条目的流进行重新排序(类似收集RegionCountries ):

           // ... collecting the intermediate map as above
            )) // Map<Source, Map<Region, List<Country>>>
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey(
                 Comparator.comparingLong(Source::getSourceId)
            ))
            .map(e -> new FinalResponse(
                    e.getKey(),
                    e.getValue().entrySet()
                     .stream()
                     .sorted(Map.Entry.comparingByKey(
                         Comparator.comparingLong(Region::getRegionId)
                     ))
                     .map(re -> new RegionCountries(re.getKey(), re.getValue()))
                     .collect(Collectors.toList()) // List<RegionCountries>
            ))
            .collect(Collectors.toList());
    

    【讨论】:

    • 如果我们想维持秩序,我们需要做什么?最终输出应该是有序的(基于增量 sourceId)。来自数据库的响应是否已根据 sourceId 排序?
    • 如果响应列表已经排序并且应该保持这种排序,则需要使用LinkedHashMap::new供应商收集地图。否则(如果输入没有预先排序,或者需要另一个排序),可以在收集结果列表时应用排序。您可以查看更新。
    猜你喜欢
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多