2016-11-10 72 views
0

我正在接受使用Java Steam API并实施标题中提到的内容的培训。
但我不满意我的代码。使用Java Stream API通过外键连接实体对象

例如,有三个实体类是简单的不可变bean。

CODE:

public class Country { 
    private final Integer countryId; // PK, primary key 
    private final String name; 

    public Country(Integer countryId, String name) { 
     this.countryId = countryId; 
     this.name = name; 
    } 
    // omitting getters 
} 

public class State { 
    private final Integer stateId; // PK 
    private final Integer countryId; // FK, foreign key 
    private final String name; 

    public State(Integer stateId, Integer countryId, String name) { 
     this.stateId = stateId; 
     this.countryId = countryId; 
     this.name = name; 
    } 
    // omitting getters 
} 

public class City { 
    private final Integer cityId; // PK 
    private final Integer stateId; // FK 
    private final String name; 

    public City(Integer cityId, Integer stateId, String name) { 
     this.cityId = cityId; 
     this.stateId = stateId; 
     this.name = name; 
    } 
    // omitting getters 
} 

这些实体的关系是一对多,不是多对多。

我想创建实体的集合Map<Country, Map<State, City>>对象使用PK和FK关系 Collection<Country>Collection<State>Collection<City>

我的实现就在这里。

CODE:

// entity collections 
Set<Country> countries = Collections.singleton(new Country(1, "America")); 
Set<State> states = Collections.singleton(new State(30, 1, "Wasington")); 
Set<City> cities = Collections.singleton(new City(500, 30, "Wasington, D.C.")); 

// intermediate maps 
Map<Integer, City> fkCityMap = cities.stream() 
    .collect(Collectors.toMap(City::getStateId, Function.identity())); 
Map<Integer, State> fkStateMap = states.stream() 
    .collect(Collectors.toMap(State::getCountryId, Function.identity())); 
Map<Integer, Map<State, City>> fkStateCityMap = fkStateMap.entrySet().stream() 
    .collect(Collectors.toMap(Entry::getKey, entry -> Collections.singletonMap(
     entry.getValue(), fkCityMap.get(entry.getValue().getStateId())))); 

// result 
Map<Country, Map<State, City>> mapWhatIWant = countries.stream() 
    .collect(Collectors.toMap(Function.identity(), 
     country -> fkStateCityMap.get(country.getCountryId()))); 

它的工作原理,但不是特别优雅评论“中间地图”的一部分,我想。
有没有更好的实现方法?


UPDATE

mistakes mentioned by Holger

  1. 我想要的类型是Map<Country, Map<State, Collection<City>>>
    Map<Country, Map<State, City>>

  2. 我误解了有关华盛顿和华盛顿
    所以,代码注释实体的集合是坏榜样。

+1

首先,你应该重新考虑你的实际任务。 “State”和“City”之间是否存在1:1映射? – Holger

+0

@霍尔格对不起,我不是英语说话的人。这是错误的命名。我会稍后更新。感谢您的评论。 –

+0

@Holger'State'和'City'之间的映射不是1:1。它是**一对多**。所以,键入我想要的是'地图<国家,地图<状态,收集 >>'如上所述您的评论。 –

回答

1

的样子,你已经从国家ID定义你的中间地图,即Map<Integer, City> fkCityMap,映射从状态ID城市,并Map<Integer, State> fkStateMap,映射国家,你正在建立的假设,可以有在Country中只有一个State,在State中只有一个City,其间接地也允许在整个Country中仅有一个City

当然,只要您的测试数据仅包含一个Country,1个State和一个City,您将不会注意到。更糟糕的是,即使您的预期结果类型Map<Country, Map<State, City>>仅适用于每State只有一个City。所以不仅你的实现被破坏了,甚至任务定义也是如此。

让我们重新定义结果类型为Map<Country, Map<State, Set<City>>>,允许每State多个City,这样你就可以实现操作

Map<Country, Map<State, Set<City>>> mapThatYouWant = countries.stream()     
    .collect(Collectors.toMap(Function.identity(), c->states.stream() 
     .filter(s -> Objects.equals(s.getCountryId(), c.getCountryId())) 
     .collect(Collectors.toMap(Function.identity(), s->cities.stream() 
      .filter(city -> Objects.equals(city.getStateId(), s.getStateId())) 
      .collect(Collectors.toSet()))))); 

但请注意,用于查找项目创建中间的地图可能是实际上更有效比多次线性搜索集合。你只需要关心创建正确的地图。

Map<Integer,Country> countryById = countries.stream() 
    .collect(Collectors.toMap(Country::getCountryId, Function.identity())); 
Map<Integer,Set<City>> citiesByStateId = cities.stream() 
    .collect(Collectors.groupingBy(City::getStateId, Collectors.toSet())); 

Map<Country, Map<State, Set<City>>> mapThatYouWant = states.stream() 
    .collect(Collectors.groupingBy(s -> countryById.get(s.getCountryId()), 
     Collectors.toMap(Function.identity(), 
      s -> citiesByStateId.getOrDefault(s.getStateId(), Collections.emptySet())))); 

顺便说一句,Washington, D.C.不在state Washington

+0

感谢您的评论。我明白我误解了。你展示的两个代码都易于阅读并清楚代码想要做什么。这是一个很好的例子,如何使用'Optional'和'groupingBy'。我会越来越多地使用您的评论研究Java Stream API。非常感谢你! –