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

将非规范化扁平化数据转为层级Java对象能否仅用Java8+Collectors单步实现

非规范化扁平化数据转层级Java对象实现方案

以下是将扁平化的城市三元组数据转换为国家-省份-城市层级对象的实现方案,包含输入输出示例、原有实现代码,以及针对单步实现问题的解答:

输入示例

[ {
  "countryName" : "USA",
  "stateName" : "FL",
  "cityName" : "Miami"
}, {
  "countryName" : "USA",
  "stateName" : "FL",
  "cityName" : "Doral"
}, {
  "countryName" : "India",
  "stateName" : "Tamilnadu",
  "cityName" : "Chennai"
}, {
  "countryName" : "India",
  "stateName" : "Karnataka",
  "cityName" : "Bangalore"
} ]

输出示例

[ {
  "countryName" : "USA",
  "states" : [ {
    "stateName" : "FL",
    "cities" : [ {
      "cityName" : "Miami"
    }, {
      "cityName" : "Doral"
    } ]
  } ]
}, {
  "countryName" : "India",
  "states" : [ {
    "stateName" : "Karnataka",
    "cities" : [ {
      "cityName" : "Bangalore"
    } ]
  }, {
    "stateName" : "Tamilnadu",
    "cities" : [ {
      "cityName" : "Chennai"
    } ]
  } ]
} ]

原有实现代码

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toSet;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.Data;

public class Normalizer {

  public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    final var objectWriter = objectMapper.writerWithDefaultPrettyPrinter();

    Tuple t1 = Tuple.of("USA", "FL", "Miami");
    Tuple t2 = Tuple.of("USA", "FL", "Doral");
    Tuple t3 = Tuple.of("India", "Tamilnadu", "Chennai");
    Tuple t4 = Tuple.of("India", "Karnataka", "Bangalore");

    List<Tuple> tuples = List.of(t1, t2, t3, t4);
    
    System.out.println("INPUT:\n" + objectWriter.writeValueAsString(tuples));
    
    System.out.println("---------------------------------------------");
    
    final var countries = normalize(tuples);
    System.out.println("OUTPUT:\n" + objectWriter.writeValueAsString(countries));
  }
  
  public static Set<Country> normalize(List<Tuple> tuples) {
    System.out.println("Step 1: Grouping");
    final var map = tuples.stream()
        .collect(groupingBy(Country::of, groupingBy(State::of, mapping(City::of, toSet()))));

    System.out.println("Step 2: Creating Object Hierarchy");
    Set<Country> countries = new HashSet<>();
    map.forEach((country, value) -> {
      countries.add(country);

      Set<State> states = new HashSet<>();
      value.forEach((state, value1) -> {
        state.setCities(value1);
        states.add(state);
      });
      country.setStates(states);
    });
    
    return countries;
  }

  @Data(staticConstructor = "of")
  public static class Tuple {

    private String countryName;
    private String stateName;
    private String cityName;

    public static Tuple of(String countryName, String stateName, String cityName) {
      Tuple t1 = new Tuple();
      t1.setCountryName(countryName);
      t1.setStateName(stateName);
      t1.setCityName(cityName);
      return t1;
    }
  }

  @Data(staticConstructor = "of")
  public static class Country {

    private String countryName;
    private Set<State> states = new HashSet<>();

    public static Country of(Tuple tuple) {
      Country country = new Country();
      country.setCountryName(tuple.getCountryName());
      return country;
    }
  }

  @Data(staticConstructor = "of")
  public static class State {

    private String stateName;
    private Set<City> cities = new HashSet<>();

    public static State of(Tuple tuple) {
      State state = new State();
      state.setStateName(tuple.getStateName());
      return state;
    }
  }

  @Data(staticConstructor = "of")
  public static class City {

    private String cityName;

    public static City of(Tuple tuple) {
      City city = new City();
      city.setCityName(tuple.getCityName());
      return city;
    }
  }
}

问题解答:单步Collectors实现方案

可以实现仅通过Java 8+的Collectors相关方法完成整个转换,不需要拆分两步执行。核心是使用collectingAndThen收集器,将分组后的结果直接做层级对象的属性赋值,整个逻辑完全封装在Stream的collect操作内。

优化后的单步normalize方法代码如下:

import static java.util.stream.Collectors.*;

public static Set<Country> normalize(List<Tuple> tuples) {
    return tuples.stream()
        .collect(collectingAndThen(
            // 原有分组逻辑不变,得到<国家, <省份, 城市集合>>的嵌套Map
            groupingBy(Country::of, groupingBy(State::of, mapping(City::of, toSet()))),
            // 对分组结果直接处理,赋值层级属性后返回国家集合
            countryToStateMap -> {
                countryToStateMap.forEach((country, stateToCityMap) -> {
                    // 给每个省份赋值对应的城市集合
                    stateToCityMap.forEach(State::setCities);
                    // 给国家赋值对应的省份集合
                    country.setStates(stateToCityMap.keySet());
                });
                return countryToStateMap.keySet();
            }
        ));
}

实现说明

  • collectingAndThen是Java 8自带的收集器,作用是接收两个参数:上游收集器、结果转换函数,刚好适合把分组得到的Map直接转换为我们需要的对象集合
  • 因为用到了Lombok的@Data注解,Country和State的equals/hashCode仅基于名称字段生成,所以分组得到的Key不会重复,直接取Key集合即可得到正确的层级对象,不需要额外去重
  • 整个实现没有中间变量暴露,所有逻辑都在collect操作内完成,符合单步实现的要求

内容的提问来源于stack exchange,提问作者Arun Chandrasekaran

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 00:48:02