【问题标题】:How to create nested maps from flat sql select result set如何从平面 sql 选择结果集中创建嵌套映射
【发布时间】:2019-12-03 07:14:48
【问题描述】:

有一个类似于以下的 SQL 查询:

SELECT DISTINCT REGION, COUNTRY, CITY

产生这个结果集

地区国家城市 欧洲 法国 巴黎 欧洲 法国 里昂 欧洲 法国 尼斯 欧洲 德国 柏林 欧洲 德国 多特蒙德 欧洲 德国 汉诺威

有没有办法使用谷歌的 ListMultimap 以便我最终得到一个键 -> 值 -> 值结构?

例如

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

或者另一个包更适合这个?

编辑:

尝试实现@Prog_G 的解决方案,但使用以下方法内存不足。我认为最后我必须将外部转换为另一个数据结构。我会以某种方式以此创建地图吗? 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);
    }
}

【问题讨论】:

  • 查看 Guava 的 Table。或者只使用Map&lt;String, Map&lt;String, List&lt;String&gt;&gt;&gt;
  • 试试这个例子:ListMultimap>> multimap = ArrayListMultimap.create(); ListMultimap> inner = ArrayListMultimap.create(); inner.put("innerkey1", Arrays.asList("value1")); multimap.put("key1", inner); System.out.println(multimap.toString());
  • @gurioso,理想情况下我想在最后创建一个地图。
  • @Prog_G 我尝试实现您的解决方案,但内存不足
  • 意思是,你的意思是这个结构{EUROPE{GERMANY{BERLIN, DORTMUND, HANNOVER}, FRANCE{PARIS, LYON, NICE}}}?

标签: java guava multimap


【解决方案1】:

stream()Collectors.groupingBy 一起使用(致谢here,另见this)。 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;
}

目前这实际上会给你HashMaps 和HashSet。他们的问题是在迭代他们的条目时缺乏保证的顺序。此外,如果结构应该支持显示在用户界面中的树,则您没有可用的重新排序或“在索引处插入”操作。例如。 LinkedHashMapTreeSet 具有可预测的迭代顺序。在创建最终的树数据结构时,这种方法仍然可以作为中间步骤。

根据您的口味,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;
}

【讨论】:

  • 非常感谢您的所有时间。我昨天已经尝试过您的代码,但效果不佳。现在尝试了更新的第二种方法,它就像一个魅力。谢谢!
  • @Bishonen_PL 这一直是我脑海中的一个问题,但由于缺乏机会,我从未接触过。所以,谢谢!
【解决方案2】:

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

或者如果你想要可变表作为结果:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2014-05-15
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多