简体   繁体   English

如何从平面 sql 选择结果集创建嵌套映射

[英]How to create nested maps from flat sql select result set

Having a SQL query similar to:具有类似于以下内容的 SQL 查询:

SELECT DISTINCT REGION, COUNTRY, CITY

Yielding this result set产生这个结果集

REGION COUNTRY CITY
EUROPE FRANCE  PARIS
EUROPE FRANCE  LYON 
EUROPE FRANCE  NICE
EUROPE GERMANY BERLIN
EUROPE GERMANY DORTMUND 
EUROPE GERMANY HANNOVER

Is there a way to use google's ListMultimap so that I end up with a key -> value -> value structure?有没有办法使用谷歌的 ListMultimap 以便我最终得到一个键 -> 值 -> 值结构?

Eg例如

{EUROPE
    {GERMANY
       {BERLIN, DORTMUND, HANNOVER}, 
     FRANCE
       {PARIS, LYON, NICE}
     }
}

Or would another package be better suited for this?或者另一个包更适合这个?

EDIT:编辑:

Tried implementing @Prog_G's solution, but got out of memory with the below approach.尝试实施@Prog_G 的解决方案,但使用以下方法内存不足。 I reckon that I have to transform outer to yet another data structure at the end.我认为最后我必须将外部转换为另一种数据结构。 Would I create a Map out of this in some way?我会以某种方式从中创建地图吗? PS.附注。 the real example is one more level deep, but wanted to keep it simpler for the sake of the question.真实的例子更深一层,但为了这个问题,想让它更简单。

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);
    }
}

You could use Table , as per Andy Turner's comment.根据安迪特纳的评论,您可以使用Table In your case it would be something like:在你的情况下,它会是这样的:

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()
        ));

Or if you want mutable table as a result:或者,如果您想要可变表作为结果:

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
        ));

Works with stream() and Collectors.groupingBy (credits go here , see also this ).与作品stream()Collectors.groupingBy (学分去这里,又见)。 Set instead of List guarantees unique elements. 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()))));

given给予

class Record {
    String city;
    String region;
    String country;
}

Currently this will give you actually HashMap s and a HashSet .目前,这实际上会给你HashMap和一个HashSet Their issue is a lack of guaranteed order when iterating their entries.他们的问题是在迭代他们的条目时缺乏保证的顺序。 Also you have no reordering or "insert at a index" operation available, if the structure should back a tree displayed in a user interface.如果结构应该支持用户界面中显示的树,则您也没有可用的重新排序或“在索引处插入”操作。 Eg LinkedHashMap and TreeSet have a predictable iteration order.例如LinkedHashMapTreeSet具有可预测的迭代顺序。 Still this approach may be ok as an intermediary step when creating the final tree data structure.在创建最终的树数据结构时,这种方法仍然可以作为中间步骤。

Depending on your taste the usage of Map.computeIfAbsent may be an option, which was introduced with an eye on multimaps .根据您的口味, Map.computeIfAbsent的使用可能是一个选项,它是在引入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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM