Having a SQL query similar to:
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?
E.g.
{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. 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);
}
}
Works with stream()
and Collectors.groupingBy
(credits go here, see also this). Set
instead of List
guarantees unique elements.
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
. 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. E.g. LinkedHashMap and TreeSet have a predictable iteration order. 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.
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;
}