如何从平面 sql select 结果集创建嵌套地图
How to create nested maps from flat sql select result set
SQL 查询类似于:
SELECT DISTINCT REGION, COUNTRY, CITY
生成此结果集
REGION COUNTRY CITY
EUROPE FRANCE PARIS
EUROPE FRANCE LYON
EUROPE FRANCE NICE
EUROPE GERMANY BERLIN
EUROPE GERMANY DORTMUND
EUROPE GERMANY HANNOVER
有没有办法使用 google 的 ListMultimap,以便我最终得到一个键 -> 值 -> 值结构?
例如
{EUROPE
{GERMANY
{BERLIN, DORTMUND, HANNOVER},
FRANCE
{PARIS, LYON, NICE}
}
}
或者另一个包是否更适合这个?
编辑:
尝试实施@Prog_G 的解决方案,但使用以下方法时内存不足。我认为最后我必须将 outer 转换为另一种数据结构。我会以某种方式从中创建地图吗?
PS。真实的例子更深一层,但为了问题的缘故想让它更简单。
private static class GeoRowMapper implements RowCallbackHandler {
ListMultimap<String, ListMultimap<String, ListMultimap<String, String>>> outer = ArrayListMultimap.create();
ListMultimap<String, ListMultimap<String, String>> middle = ArrayListMultimap.create();
ListMultimap<String, String> inner = ArrayListMultimap.create();
@Override
public void processRow(ResultSet rs) throws SQLException {
String region = rs.getString("region");
String country = rs.getString("country");
String city = rs.getString("city");
String address = rs.getString("address");
inner.put(city, address);
middle.put(country, inner);
outer.put(region, middle);
}
public ListMultimap<String, ListMultimap<String, ListMultimap<String, List<String>>>> get() {
return Multimaps.asMap(outer);
}
}
根据 Andy Turner 的评论,您可以使用 Table
。在你的情况下,它会是这样的:
ImmutableTable<Region, Country, ImmutableList<City>> immutableTable = RECORDS.stream()
.collect(toImmutableTable(
r -> r.getRegion(),
r -> r.getCountry(),
r -> ImmutableList.of(r.getCity()),
(l, l2) -> ImmutableList.<City>builder().addAll(l).addAll(l2).build()
));
或者如果你想要 mutable table 作为结果:
Table<Region, Country, List<City>> table = RECORDS.stream()
.collect(toTable(
r -> r.getRegion(),
r -> r.getCountry(),
r -> Lists.newArrayList(r.getCity()),
(l, l2) -> {
l.addAll(l2);
return l;
},
HashBasedTable::create
));
与 stream()
和 Collectors.groupingBy
一起工作(致谢 here, see also )。 Set
而不是 List
保证唯一元素。
Map<String, Map<String, Set<String>>> grouped =
records.stream()
.collect(Collectors.groupingBy(r -> r.region,
Collectors.groupingBy(r -> r.country,
Collectors.mapping(r -> r.city, Collectors.toSet()))));
给出
class Record {
String city;
String region;
String country;
}
目前这实际上会给你 HashMap
s 和 HashSet
。他们的问题是在迭代条目时缺乏保证顺序。如果结构应该支持用户界面中显示的树,那么您也没有可用的重新排序或 "insert at a index" 操作。例如。 LinkedHashMap and TreeSet 具有可预测的迭代顺序。在创建最终树数据结构时,这种方法仍然可以作为中间步骤。
根据您的喜好,Map.computeIfAbsent
的用法可能是一个选项,它是在 an eye on multimaps 中引入的。
public Map<String, Map<String, Set<String>>> mumap(List<Record> records) {
Map<String, Map<String, Set<String>>> result = new LinkedHashMap<>();
for (Record r : records) {
result.computeIfAbsent(r.region, region -> new LinkedHashMap<>())
.computeIfAbsent(r.country, country -> new TreeSet<>())
.add(r.city);
}
return result;
}
SQL 查询类似于:
SELECT DISTINCT REGION, COUNTRY, CITY
生成此结果集
REGION COUNTRY CITY EUROPE FRANCE PARIS EUROPE FRANCE LYON EUROPE FRANCE NICE EUROPE GERMANY BERLIN EUROPE GERMANY DORTMUND EUROPE GERMANY HANNOVER
有没有办法使用 google 的 ListMultimap,以便我最终得到一个键 -> 值 -> 值结构?
例如
{EUROPE
{GERMANY
{BERLIN, DORTMUND, HANNOVER},
FRANCE
{PARIS, LYON, NICE}
}
}
或者另一个包是否更适合这个?
编辑:
尝试实施@Prog_G 的解决方案,但使用以下方法时内存不足。我认为最后我必须将 outer 转换为另一种数据结构。我会以某种方式从中创建地图吗? PS。真实的例子更深一层,但为了问题的缘故想让它更简单。
private static class GeoRowMapper implements RowCallbackHandler {
ListMultimap<String, ListMultimap<String, ListMultimap<String, String>>> outer = ArrayListMultimap.create();
ListMultimap<String, ListMultimap<String, String>> middle = ArrayListMultimap.create();
ListMultimap<String, String> inner = ArrayListMultimap.create();
@Override
public void processRow(ResultSet rs) throws SQLException {
String region = rs.getString("region");
String country = rs.getString("country");
String city = rs.getString("city");
String address = rs.getString("address");
inner.put(city, address);
middle.put(country, inner);
outer.put(region, middle);
}
public ListMultimap<String, ListMultimap<String, ListMultimap<String, List<String>>>> get() {
return Multimaps.asMap(outer);
}
}
根据 Andy Turner 的评论,您可以使用 Table
。在你的情况下,它会是这样的:
ImmutableTable<Region, Country, ImmutableList<City>> immutableTable = RECORDS.stream()
.collect(toImmutableTable(
r -> r.getRegion(),
r -> r.getCountry(),
r -> ImmutableList.of(r.getCity()),
(l, l2) -> ImmutableList.<City>builder().addAll(l).addAll(l2).build()
));
或者如果你想要 mutable table 作为结果:
Table<Region, Country, List<City>> table = RECORDS.stream()
.collect(toTable(
r -> r.getRegion(),
r -> r.getCountry(),
r -> Lists.newArrayList(r.getCity()),
(l, l2) -> {
l.addAll(l2);
return l;
},
HashBasedTable::create
));
与 stream()
和 Collectors.groupingBy
一起工作(致谢 here, see also Set
而不是 List
保证唯一元素。
Map<String, Map<String, Set<String>>> grouped =
records.stream()
.collect(Collectors.groupingBy(r -> r.region,
Collectors.groupingBy(r -> r.country,
Collectors.mapping(r -> r.city, Collectors.toSet()))));
给出
class Record {
String city;
String region;
String country;
}
目前这实际上会给你 HashMap
s 和 HashSet
。他们的问题是在迭代条目时缺乏保证顺序。如果结构应该支持用户界面中显示的树,那么您也没有可用的重新排序或 "insert at a index" 操作。例如。 LinkedHashMap and TreeSet 具有可预测的迭代顺序。在创建最终树数据结构时,这种方法仍然可以作为中间步骤。
根据您的喜好,Map.computeIfAbsent
的用法可能是一个选项,它是在 an eye on multimaps 中引入的。
public Map<String, Map<String, Set<String>>> mumap(List<Record> records) {
Map<String, Map<String, Set<String>>> result = new LinkedHashMap<>();
for (Record r : records) {
result.computeIfAbsent(r.region, region -> new LinkedHashMap<>())
.computeIfAbsent(r.country, country -> new TreeSet<>())
.add(r.city);
}
return result;
}