Java – Sort Map By Value

In Java, we can use the TreeMap class to sort a map by its keys. This class is very handy to use. However, sometimes we need to sort a map by its values. How to sort a map by its values is a most frequently asked question by Java programmers. In this post, I will develop the best way to write such a method.

1. Naive Method

The following is a solution to sort a map of <String, Integer> pairs. This is often used in counting frequency of words.

import java.util.Comparator;
import java.util.HashMap;
import java.util.TreeMap;
 
public class SortMapByValue {
 
	public static void main(String[] args) {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
		TreeMap<String, Integer> sortedMap = sortMapByValue(map);  
		System.out.println(sortedMap);
	}
 
	public static TreeMap<String, Integer> sortMapByValue(HashMap<String, Integer> map){
		Comparator<String> comparator = new ValueComparator(map);
		//TreeMap is a map sorted by its keys. 
		//The comparator is used to sort the TreeMap by keys. 
		TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator);
		result.putAll(map);
		return result;
	}
}

Here is the comparator class implementation.

// a comparator that compares Strings
class ValueComparator implements Comparator<String>{
 
	HashMap<String, Integer> map = new HashMap<String, Integer>();
 
	public ValueComparator(HashMap<String, Integer> map){
		this.map.putAll(map);
	}
 
	@Override
	public int compare(String s1, String s2) {
		if(map.get(s1) >= map.get(s2)){
			return -1;
		}else{
			return 1;
		}	
	}
}

In this solution, we use a TreeMap to sort the map. When creating the TreeMap, we give it a comparator. The comparator accepts strings and compare the assocated values of the given string key in the map.

The method works well, but it only works for sorting String and Integer pairs. If we want to sort a map with other types of keys and values, it needs to be rewritten. Therefore, a more general solution is preferred.

2. More General Solution

We can ignore the generic type and make the methods work for any types like the following.

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
 
public class Solution {
	public static void main(String[] args) {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
		Map sortedMap = sortByValue(map);
		System.out.println(sortedMap);
	}
 
	public static Map sortByValue(Map unsortedMap) {
		Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
		sortedMap.putAll(unsortedMap);
		return sortedMap;
	}
 
}
 
class ValueComparator implements Comparator {
	Map map;
 
	public ValueComparator(Map map) {
		this.map = map;
	}
 
	public int compare(Object keyA, Object keyB) {
		Comparable valueA = (Comparable) map.get(keyA);
		Comparable valueB = (Comparable) map.get(keyB);
		return valueB.compareTo(valueA);
	}
}

The solution is not type-safe, we need a type-safe and general solution.

3. Using Generic Types

public class SortMapByValue {
 
	public static void main(String[] args) {
		// <String, Integer> Map
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("a", 10);
		map.put("b", 30);
		map.put("c", 50);
		map.put("d", 40);
		map.put("e", 20);
		System.out.println(map);
 
 
		Comparator<String> comparator = new ValueComparator<String, Integer>(map);
		TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator);
		result.putAll(map);
 
		System.out.println(result);
 
		// <Integer, Integer> Map
 
		HashMap<Integer, Integer> map2 = new HashMap<Integer, Integer>();
		map2.put(1, 10);
		map2.put(2, 30);
		map2.put(3, 50);
		map2.put(4, 40);
		map2.put(5, 20);
		System.out.println(map2);
 
		Comparator<Integer> comparator2 = new ValueComparator<Integer, Integer>(map2);
		TreeMap<Integer, Integer> result2 = new TreeMap<Integer, Integer>(comparator2);
		result2.putAll(map2);
 
		System.out.println(result2);
 
	}
 
}
// a comparator using generic type
class ValueComparator<K, V extends Comparable<V>> implements Comparator<K>{
 
	HashMap<K, V> map = new HashMap<K, V>();
 
	public ValueComparator(HashMap<K, V> map){
		this.map.putAll(map);
	}
 
	@Override
	public int compare(K s1, K s2) {
		return -map.get(s1).compareTo(map.get(s2));//descending order	
	}
}

4. Another Way of Using Generic Types

public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
	List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
	Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
		@Override
		public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
			return (e1.getValue()).compareTo(e2.getValue());
		}
	});
 
	Map<K, V> result = new LinkedHashMap<>();
	for (Map.Entry<K, V> entry : list) {
		result.put(entry.getKey(), entry.getValue());
	}
 
	return result;
}

5. Simple

When need a simple solution, sorting can be done in one line.

LinkedList<Map.Entry<String, Integer>> list = new LinkedList<>(counter.entrySet());
Comparator<Map.Entry<String, Integer>> comparator = Comparator.comparing(Map.Entry::getValue);
Collections.sort(list, comparator.reversed());

9 thoughts on “Java – Sort Map By Value”

  1. You’re right
    To make other examples work I have to add some code to avoid that Comparator.compare() returns a zero.
    like this:

    return valueB.compareTo(valueA) > 0 ? 1 : -1;

  2. Only first solutions works well. If you want to sort a Map with many the same values then they will be deleted. for examplehere D will be delete:

    HashMap hm = new HashMap();
    hm.put(“A”, 8);
    hm.put(“B”, 4);
    hm.put(“C”, 9);
    hm.put(“D”, 4);

  3. There is a bug in example 3. If two or more k/v pair have the same value the only one k/v pair is added to result.

  4. In “2. More General Solution”, why are you passing unsorted map essentially TWO times?

    here (1):

    Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));

    and here(2):
    sortedMap.putAll(unsortedMap);
    Why do you do (2) ? To rely (while comparing) on whatever values there are in the initial unsortedMap? But..isn’t this solution error-prone? Who guarantees that in (2) the map that you pass will contain the same keys? Also, isn’t this solution making the client of the code remember that they also need to pass in .putAll() an unsortedMap once again?

  5. The provided solution will return a NullPointerException if we tried to get a object using invalid (not found) key. Since the get method internally calls the compare method which tries to get the value from the map using the invalid key.

  6. Thanks, I think this is the most simple way to sort Hashmap by value, you only need to worry about classes which do not implement comparator.

  7. Note: below code will work to sort HashMap values.. no need to worry about generics.

    import java.util.*;

    public class SortHashMapValues{
    public static void main(String []args){
    Map map=new HashMap();
    map.put(“one”,1);
    map.put(“ten”,10);
    map.put(“three”,3);
    map.put(“two”,2);

    List list=new ArrayList(map.entrySet());

    Collections.sort(list,new Comparator(){
    public int compare(Object obj1, Object obj2){
    return ((Comparable)((Map.Entry)(obj1)).getValue

    ()).compareTo(((Map.Entry)(obj2)).getValue());
    }
    });
    System.out.println(list);
    }
    }

  8. This is interesting but there is many warning because there is no generic type. This is a version of More General Solution with all the generic type :

    package misc;

    import java.util.Comparator;
    import java.util.Map;
    import java.util.TreeMap;

    public class ArrayTools
    {
    private static class ValueComparator<K , V extends Comparable> implements Comparator
    {
    Map map;

    public ValueComparator(Map map) {
    this.map = map;
    }

    @Override
    public int compare(K keyA, K keyB) {
    Comparable valueA = map.get(keyA);
    V valueB = map.get(keyB);
    return valueA.compareTo(valueB);
    }

    }

    public static<K, V extends Comparable> Map sortByValue(Map unsortedMap)
    {
    Map sortedMap = new
    TreeMap(new ValueComparator(unsortedMap));
    sortedMap.putAll(unsortedMap);
    return sortedMap;
    }
    }

Leave a Comment