How to Sort a HashMap by Key in Java

In this tutorial, we'll take a look at how to sort a HashMap by key in Java.

Let's go ahead and create a simple HashMap:

Map<String, Integer> unsortedMap = new HashMap();

unsortedMap.put("John", 21);
unsortedMap.put("Maria", 34);
unsortedMap.put("Mark", 31);
unsortedMap.put("Sydney", 24);

unsortedMap.entrySet().forEach(System.out::println);

We've got Strings as keys, and Integers as values. Most of the time, you'll encounter Integers or Strings as keys, and custom objects, Strings or Integers as values. We'll want to sort this HashMap, based on the String keys.

HashMaps don't guarantee to maintain the order of its elements in any case. The order can change through time, and they most definitely won't be printed back in the order of insertion:

John=21
Mark=31
Maria=34
Sydney=24

If you re-run this program, it'll keep this order, since HashMaps order their elements into bins, based on the hash value of the keys. When printing values from a HashMap, its contents are printed sequentially, so the results will stay the same if we re-run the program multiple times.

Sort HashMap by Key with TreeMap

TreeMap extends the SortedMap interface, unlike the HashMap implementation. TreeMaps are meant to be the sorted counterpart, however, TreeMaps only sort by keys, given a comparator.

Sort String Keys Lexicographically

Creating a TreeMap, given a HashMap is as easy as supplying the constructor call with the unsorted map:

Map<String, Integer> sortedMap = new TreeMap<>(unsortedMap);
sortedMap.entrySet().forEach(System.out::println);

Running this code results in:

John=21
Maria=34
Mark=31
Sydney=24

Since we didn't supply any comparator, the default comparator used for Strings kicks in. Specifically, when you compare Strings, the compareTo() method compares the lexicographical value of each String and sorts them in ascending order.

We'll see names starting with A, before names starting with B, etc. Let's add two new names and see what happens:

unsortedMap.put("Adam", 35);
unsortedMap.put("Aaron", 22);
        
Map<String, Integer> sortedMap = new TreeMap<>(unsortedMap);
sortedMap.entrySet().forEach(System.out::println);

This results in:

Aaron=22
Adam=35
John=21
Maria=34
Mark=31
Sydney=24

Sort Keys with Custom Comparator

A really nice feature is that we can supply a new Comparator<T>() to the TreeMap and specify our own comparing logic in it. For example, let's take a look at how we can sort String keys by length in a HashMap, using the length of the Strings, and a custom comparator:

Map<String, Integer> sortedMap = new TreeMap<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        int lengthDifference = o1.length() - o2.length();
        if (lengthDifference != 0) return lengthDifference;
        return o1.compareTo(o2);
    }
});

sortedMap.putAll(unsortedMap);

sortedMap.entrySet().forEach(System.out::println);

Here, we've constructed a TreeMap with a custom Comparator and in the overridden compare() method, we've specified our desired logic.

Since we have no guarantee that o1.length() - o2.length() won't be 0, a simple if-statement makes sure we compare them lexicographically if their lengths are the same.

Then, once we've specified the sorting criteria for the TreeMap, we've used the putAll() method to insert all the elements from the unsortedMap into the sortedMap.

Running this code results in:

Adam=35
John=21
Mark=31
Aaron=22
Maria=34
Sydney=24

The map is now sorted via a custom Comparator, which in this case compares the lengths of the String keys. You can use any logic here to accommodate your specific needs.

Sort HashMap by Key with LinkedHashMap

LinkedHashMap preserves the order of insertion. It keeps a doubly-linked list of all entries, allowing you to very naturally access and iterate over its elements.

So, the easiest way to convert an unsorted HashMap into a LinkedHashMap is to add the elements in the order we'd like them to be in.

Sort HashMap Keys Lexicographically

Now, let's go ahead and sort the unsortedMap, by creating a new LinkedHashMap that'll contain the elements, in sorted order.

The Map.Entry class has a very handy method that comes into play here - comparingByKey(), which compares the keys if they've got valid comparison methods. Since we're dealing with Strings, this is the compareTo() method, which will once again sort the Strings lexicographically:

Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (a, b) -> { throw new AssertionError(); },
                LinkedHashMap::new
        ));

sortedMap.entrySet().forEach(System.out::println);

What we've done here is streamed the unsortedMap's set of Map.Entry objects. Then, using the sorted() method, we've supplied the handy Comparator generated by comparingByKey(), which compares the given objects with their default comparison implementation.

Once sorted, we collect() them, using Collectors.toMap(), into a new map. Of course, we'll be using the same keys and values from the original map, via the Map.Entry::getKey and Map.Entry::getValue method references.

Finally, a new LinkedHashMap is instantiated, into which all of these elements, in sorted order, are inserted.

Running this code results in:

Aaron=22
Adam=35
John=21
Maria=34
Mark=31
Sydney=24

Sort HashMap Keys with Custom Comparator

Alternatively, you can use your own Comparator instead of the one generated by Map.Entry.comparingByKey(). This is as easy as supplying a Comparator.comparing() and passing in a valid Lambda Expression into it:

Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
        .sorted(Comparator.comparing(e -> e.getKey().length()))
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (a, b) -> { throw new AssertionError(); },
                LinkedHashMap::new
        ));

sortedMap.entrySet().forEach(System.out::println);

Here, we've recreated our custom comparator that sorts keys by their value from earlier sections. Now, the String keys will be sorted by their length instead of their lexicographical value:

Adam=35
John=21
Mark=31
Aaron=22
Maria=34
Sydney=24

Of course, you can easily switch from ascending order to descending order by simply adding a - in front of the e.getKey().length():

Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
        .sorted(Comparator.comparing(e -> -e.getKey().length()))
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (a, b) -> { throw new AssertionError(); },
                LinkedHashMap::new
        ));

sortedMap.entrySet().forEach(System.out::println);

This results in:

Sydney=24
Aaron=22
Maria=34
Adam=35
John=21
Mark=31

Additionally, you can use other comparators, such as Comparator.comparingInt() if you're dealing with Integer values (we are here, though, a general comparator also works), Comparator.comparingDouble() or Comparator.comparingLong() to suit your needs.

Conclusion

In this tutorial, we've gone over how to sort a Java HashMap by Key. We've initially used a TreeMap to sort and maintain the order of the sorted entries, both using the default and custom comparator.

Then, we've Java 8 Streams with the LinkedHashMap class to achieve this functionality as well, both for default and custom comparators in ascending and descending order.