Sort the outer map by the value of the nested map

2019-04-17 16:42发布

问题:

Sorting the outer map Map<String, Map<String, List<Integer>>> by the list size in the nested map retaining the outer and inner keys as before.

回答1:

You may solve this by generalizing the process:

private static <K,V,R> Map<K,R>
                       replaceAndSortValues(Map<K,V> m, Function<V,R> f, Comparator<R> c) {
    return m.entrySet().stream()
        .map(e -> Map.entry(e.getKey(), f.apply(e.getValue())))
        .sorted(Map.Entry.comparingByValue(c.reversed()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                                  (a,b) -> { throw new AssertionError(); },
                                  LinkedHashMap::new));
}

This method creates a new map with the same keys as the specified one, replacing all values using the specified function and sorting the entries according to the reversal of the specified comparator. It uses Java 9’s Map.entry(…, …) factory. If you have to support Java 8 or null keys or values, you may use new AbstractMap.SimpleImmutableEntry<>(…, …) instead.

This method can now be used to replace you inner map’s Lists with Integers representing their sizes and sort them in descending order and use the replacement operation as replacement function of the outer map:

public static Map<String, Map<String, Integer>>
              getCallWithStateSizeGroup(ThreadDumpDo threadDumpDo) {
    return replaceAndSortValues(getCallStackWithStateGroup(threadDumpDo),
        m -> replaceAndSortValues(m, List::size, Comparator.<Integer>naturalOrder()),
        Comparator.comparing(m -> m.values().iterator().next()));
}

This does basically the same as your posted solution. The outer map’s comparator uses the fact that the new inner maps are already sorted, so their first value is the maximum. But there must be no empty inner map.

This could easily get adapted to keep the List<ThreadDo> and just sort them by size:

public static Map<String, Map<String, List<ThreadDo>>>
              getCallWithStateSizeGroup(ThreadDumpDo threadDumpDo) {
    return replaceAndSortValues(getCallStackWithStateGroup(threadDumpDo),
        m -> replaceAndSortValues(m, Function.identity(),
                                  Comparator.comparingInt(List::size)),
        Comparator.comparingInt(m -> m.values().iterator().next().size()));
}

We only have to change the inner map’s replacement function to Function.identity() and provide a comparator using the list’s sizes. The outer map’s comparator still can use the fact that the inner maps are already sorted at this point, but also has to extract the size() of the list for comparison.