Thursday, July 16, 2020

How to Sort an HashMap by values in Java 8 - Example Tutorial

In the last article, I have shown you how to sort a Map in Java 8 by keys, and today, I'll teach you how to sort a Map by values using Java 8 features e.g. lambda expression, method reference, streams, and new methods added into the java.util.Comparator and Map.Entry classes. In order to sort any Map, like HashMap, Hashtable, LinkedHashMap, TreemMap, or even ConcurrentHashMap, you can first get a set of entries by using the entrySet() method and then you can get the stream by calling the stream() method. The entrySet()  method returns a Set which inherit the stream() method from the java.util.Collection class. Once you got the stream, you can just call the sorted() method which can sort all Map.Entry objects available in Stream using a Comparator.

In order to compare entries of a Map by values, you can use the newly added Map.Entry.comparingByValue() method from the java.util.Map.Entry class.

This is the counterpart of comparingByKey() method which we have used in the last article. Both of these methods are overloaded to work with both Comparable and Comparator objects.

Once you sort the stream, you can do whatever you want to do, like, if you just want to print keys, values, or entries in sorted order, just use the forEach() method or if you want a Map which is sorted on values then you can use the collect() method of stream class.

This method accepts a Collector and allows you to capture all elements of Stream into whatever collection you want to. For example, if you want a sorted map then you can use the toMap() method of java.util.stream.Collectors class.



This method is overloaded and provides a couple of choices, for example, you can collect entries in any kind of map or you can also specify the kind of map you want like to keep entries in sorted order, we'll use the LinkedHashMap. It also allows you to break ties in case of the same values like you can arrange them in the order you want to.

And, If you are serious about improving Java functional programming skills then I highly recommend you check out the  Learn Java Functional Programming with Lambdas & Streams by Ranga Rao Karnam on Udemy, which explains both Functional Programming and Java Stream fundamentals in good detail

Java 8 - Sorting HashMap by values in ascending and descending order



Steps to Sort a HashMap by Values in Java 8


In short, here are the exact steps to sort a HashMap in Java 8 by values in ascending or descending order, assuming you already have a map object :

1. Get the set of entries by calling the Map.entrySet() method.

2. Get the stream of entries by calling stream() method.

3. Call the sorted method with a Comparator.
4. Use the Map.Entry.comparingByValue() comparator to sort entries by values.

5. Collect the result using the collect() method.
6. Use Collectors.toMap() method to get the result in another Map. 

7. Provide LinkedHashMap::new to the last parameter to force it to return a LinkedHashMap, to keep the sorted order preserved.

8. In order to sort in decreasing order, just reverse the order of Comparator using Collections.reverseOrder() or Comparator.reverse() method of Java 8.  

This is the new method added into the Comparator class in Java SE 8. You can further see The Complete Java MasterClass for the full list of new methods added into key Java classes like Java Collection Framework, String, and Comparator, etc. 

How to Sort an HashMap by Values in Ascending order in Java 8

s Once you follow this step you will get a Map that is sorted by values. Now that you know the theory and steps, let's see the code example in the next section to get it right.



Java Program to sort a HashMap by Values (Ascending and Descending Order)

Here is our complete Java program to sort a Map by values using Java 8 features e.g. new methods on existing classes in Java 8 by evolving them using default methods and static methods on interfaces. In this example, I have got a Map of the map of items and their expenses like rent, utility, transportation, etc.

The Map key is String, which represents item and value is Integer, which is expenses. Our task is to sort the Map by values to find out which item cost us most and print all items in their decreasing order of values.

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static java.util.stream.Collectors.*;
import static java.util.Map.Entry.*;

/*
 * Java Program to sort a Map by values in Java 8
 * 
 */
public class Main {

  public static void main(String[] args) throws Exception {

    // a Map with string keys and integer values
    Map<String, Integer> budget = new HashMap<>();
    budget.put("clothes", 120);
    budget.put("grocery", 150);
    budget.put("transportation", 100);
    budget.put("utility", 130);
    budget.put("rent", 1150);
    budget.put("miscellneous", 90);

    System.out.println("map before sorting: " + budget);

    // let's sort this map by values first
    Map<String, Integer> sorted = budget
        .entrySet()
        .stream()
        .sorted(comparingByValue())
        .collect(
            toMap(e -> e.getKey(), e -> e.getValue(), (e1, e2) -> e2,
                LinkedHashMap::new));

    System.out.println("map after sorting by values: " + sorted);

    // above code can be cleaned a bit by using method reference
    sorted = budget
        .entrySet()
        .stream()
        .sorted(comparingByValue())
        .collect(
            toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                LinkedHashMap::new));

    // now let's sort the map in decreasing order of value
    sorted = budget
        .entrySet()
        .stream()
        .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
        .collect(
            toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                LinkedHashMap::new));

    System.out.println("map after sorting by values in descending order: "
        + sorted);
  }

}

Output
map before sorting: {grocery=150, utility=130, miscellneous=90, rent=1150,
 clothes=120, transportation=100}
map after sorting by values: {miscellneous=90, transportation=100,
 clothes=120, utility=130, grocery=150, rent=1150}
map after sorting by values in descending order: {rent=1150, grocery=150,
 utility=130, clothes=120, transportation=100, miscellneous=90}

You can see that before sorting the map has a random order in terms of values but first, we have sorted them in the increasing order of values and later we have sorted the same Map in the decreasing order of values, that's why rent comes first because it cost us highest.

Some tips

1) Use static import to shorten the code, when you use the static import feature to import both Map.Entry and java.util.stream.Collectors classes you can refer their methods without including class name like instead of Collectors.toMap() you can just use toMap().

2) Use method reference in place of lambda expression wherever you can. See this article to learn more about how to convert lambda expression to method reference in Java 8, if you are not familiar with that.


That's all about how to sort a Map by values in Java 8. You can see that it's so easy to sort the Map using new methods added to existing classes. All that is possible because of the default method feature of JDK 8, which allows you to add new methods to existing classes.

Before this enhancement, it wasn't possible in Java without breaking the existing client of interfaces because as soon as you add a new method to an interface, all its clients had to implement it.

This is not required anymore if the method is default or static because they are not abstract but concrete methods.


Further Learning
The Complete Java MasterClass
What's New in Java 8
Refactoring to Java 8 Streams and Lambdas Self- Study Workshop



Related Java 8 Tutorials

If you are interested in learning more about new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8:
  • 5 Books to Learn Java 8 from Scratch (books)
  • What is the default method in Java 8? (example)
  • Top 5 Courses to learn Java 8 in depth (courses)
  • How to join String in Java 8 (example)
  • Top 5 Courses to become a full-stack Java developer (courses)
  • How to use filter() method in Java 8 (tutorial)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • How to use Stream class in Java 8 (tutorial)
  • 10 Free Courses to learn Spring Framework for Beginners (courses)
  • How to convert List to Map in Java 8 (solution)
  • Difference between abstract class and interface in Java 8? (answer)
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • How to use peek() method in Java 8 (example)
  • How to sort the map by keys in Java 8? (example)
  • How to sort the may by values in Java 8? (example)
  • 10 examples of Options in Java 8? (example)
  • 5 Courses to learn Functional Programming in Java (courses)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions or suggestions then please drop a comment.

P.S.: If you just want to learn more about new features in Java 8 then please see the What's New in Java 8 online course on Pluralsight. It explains all the important features of Java 8 like lambda expressions, streams, functional interfaces, Optional, new Date Time API, and other miscellaneous changes.

5 comments :

Ibrahim said...

Map sorted = budget
.entrySet()
.stream()
.sorted(comparingByValue())
.collect(
toMap(e -> e.getKey(), e -> e.getValue(), (e1, e2) -> e2,
LinkedHashMap::new)); in this line for using comparingByValue() it is expecting Map.Entry.comparingByValue(). could you please check and let me know the answer.

Triveni said...

if you are using static import then direct comparingByValue() method will work.

import: import static java.util.Map.Entry.*;

javin paul said...

Yes, that's the benefit of static import, it makes your code shorter and concise

Anonymous said...

Your code not working

javin paul said...

Hello what error are you getting?

Post a Comment