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 String
s as keys, and Integer
s as values. Most of the time, you'll encounter Integer
s or String
s as keys, and custom objects, String
s or Integer
s as values. We'll want to sort this HashMap
, based on the String
keys.
HashMap
s 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 HashMap
s 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. TreeMap
s are meant to be the sorted counterpart, however, TreeMap
s 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 length
s 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.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
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 String
s, this is the compareTo()
method, which will once again sort the String
s 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.