In this tutorial, we'll take a look at how to sort a HashMap by value 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. And we'd like to sort this map based on the values.
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.
Note: 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 HashMap by Value 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 in Ascending Order
To sort the unsortedMap
we've seen earlier, we'll create a new LinkedHashMap
to house the elements in the order we want them to be in.
Let's start off with sorting a HashMap
in ascending order:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> e.getValue()))
.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 can use various Comparator
s to specify how the entries are compared.
Since we're dealing with simple integers, we can easily use Comparator.comparingInt()
method, and pass in a Lambda Expression. Through this expression, we supply the sort key from type T
(in our case, Integer
). This method then returns a Comparator
that compares that sort key.
Once they're sorted, we can collect()
them into a new map, via the Collectors.toMap()
call, where we use the same Map.Entry::getKey
and Map.Entry::getValue
as in the unsortedMap
.
Finally, a new LinkedHashMap
is instantiated, into which all of these elements, in sorted order, are inserted.
Running this code results in:
John=21
Sydney=24
Mark=31
Maria=34
Alternatively, instead of Comparator.comparingInt()
, you can use Map.Entry.comparingByValue()
:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
However, with this approach, you can't specify your own logic for the comparisons. Comparable values, like Integers, are sorted with the under-the-hood implementation. You could specify a custom object and specify your own comparison logic in that class, though.
In a very similar vein to this, you can also simply use the Map.Entry::getValue
as well:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(Map.Entry::getValue))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> { throw new AssertionError(); },
LinkedHashMap::new
));
sortedMap.entrySet().forEach(System.out::println);
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!
This also returns:
John=21
Sydney=24
Mark=31
Maria=34
This one is functionally the exact same as the previous one, as Map.Entry.comparingByValue()
uses the getValue()
method to compare entries anyway.
Sort HashMap in Descending Order
Now, let's sort the unsorted HashMap
in descending order instead. The only difference you need to make is the Lambda Expression we've supplied to the Comparator.comparingInt()
method - we'll just use -e.getValue()
instead:
Map<String, Integer> sortedMap = unsortedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> -e.getValue()))
.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:
Maria=34
Mark=31
Sydney=24
John=21
This is the added benefit of using this approach, instead of either Map.Entry.comparingByValue()
or Map.Entry::getValue
. You can easily switch between descending and ascending order.
Conclusion
In this tutorial, we've gone over how to sort a Java HashMap by Value. We've used Java 8 Streams with the LinkedHashMap
class to achieve this functionality, both to sort by ascending and descending values.