13

I have a HashMap like this:

Map<String, List<String>> map = new HashMap<>();

map.put("USA", Arrays.asList("CA","IA","IL"));
map.put("India", Arrays.asList("MUM","CAL"));
map.put("Canada", Arrays.asList("TOR"));

I want to sort the map depending on the size of the list value, in ascending order. How can I do that?

In this case, I would like the keys to be ordered Canada, India, USA.

Michael
  • 41,989
  • 11
  • 82
  • 128
Md Johirul Islam
  • 5,042
  • 4
  • 23
  • 56
  • 9
    You can't sort a HashMap, because HashMaps don't support ordering. You'll have to put them into a data structure that does support ordering (i.e. List, LinkedHashMap) if you want to sort them. – Mshnik Jun 15 '15 at 19:08

4 Answers4

24

HashMap does not have a guaranteed iteration order so you will need to collect to a LinkedHashMap in order for the sorting to be meaningful.

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toMap;

Map<String, List<String>> sorted = map.entrySet().stream()
    .sorted(comparingInt(e -> e.getValue().size()))
    .collect(toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (a, b) -> { throw new AssertionError(); },
        LinkedHashMap::new
    )); 

The AssertionError is thrown because a combiner function is only used on parallel streams, which we are not using.

You can also use comparingByValue if you find it more readable:

import static java.util.Map.Entry.comparingByValue;

Map<String, List<String>> sorted = map.entrySet().stream()
    .sorted(comparingByValue(comparingInt(List::size)))
    // ... as above
Misha
  • 27,433
  • 6
  • 62
  • 78
  • @JGFMK `import static java.util.Comparator.comparingInt;` – Misha Jan 14 '20 at 15:32
  • 2
    The first code works a treat. The latter doesn't. Also if you want size descending use ...................................... `(e -> -e.getValue().size())` in the `comparingInt`. You can see latter doesn't here: https://code.sololearn.com/ccz02MbYMYs1/#java [gives 2 compilation errors: 1) cannot find symbol for comparingByValue 2) argument mismatch; invalid method reference] – JGFMK Jan 14 '20 at 16:48
  • 1
    Ah, yes, I didn't specify `import static java.util.Map.Entry.comparingByValue;` Good catch, I will fix it – Misha Jan 14 '20 at 17:18
  • That still didn't work. Got a bit further. https://code.sololearn.com/cJPV8XUUW9ce/#java error: incompatible types: Stream>> cannot be converted to Map> .sorted(comparingByValue(comparingInt(List::size))); ^ – JGFMK Jan 14 '20 at 23:28
  • You cannot assign a stream to a map. If you want to make a map, you must use `collect(toMap(...))` as in the first example. Somebody edited the original answer and removed the explanation that `comparingbyValue(comparingInt(..))` is only a replacement for `comparingInt(e -> ...)`. I will try to find time to roll back their edit and fix this. – Misha Jan 15 '20 at 00:02
  • I think you can get there from profile > all actions > revisions... – JGFMK Jan 15 '20 at 08:33
2

public static <K,V extends Collection> Map<K,V> sortMap(Map<K,V> map){
        return map.entrySet().stream()
                .sorted((e1, e2) -> Integer.compare(e2.getValue().size(), e1.getValue().size()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    }
DD.
  • 21,498
  • 52
  • 157
  • 246
1

You have two problems.

  1. Map doesn't support sorting.

  2. SortedMap doesn't support sorting on values only sorting on keys.

As a result of this using a Map or SortedMap isn't going to help you. What you need to do is iterate over you map and put each Entry<String, ArrayList<String>> into a collection such as a List and then sort the list with a custom compare. See this example TreeMap sort by value or this example Sorting LinkedHashMap

Community
  • 1
  • 1
bhspencer
  • 13,086
  • 5
  • 35
  • 44
1

You can simply map the hash to list of Pair() and then sort the list

val map: MutableMap<String, List<String>> = HashMap()

map["USA"] = listOf("CA", "IA", "IL")
map["India"] = listOf("MUM", "CAL")
map["Canada"] = listOf("TOR")

val sortedPairs = map.map { Pair(it.key, it.value) }.sortedBy { it.second.size }.toMap()

println("sorted List: $sortedPairs")