Sorting maps in Java based on it’s values

Ever needed to sort a Map type collection in Java ?

Update: how do to this with generics?

Well i had to the other day and of course all the documentation pointed me in the direction of the SortedMap interface.

However, this datastructure is sorted on its keys and not on its values, which was really what I wanted to accomplish, so how do you do that? After a bit of fiddeling I found something that worked. Maybe its useful to you:

public class MapValueSort {
	
	/** inner class to do soring of the map **/
	private static class ValueComparer implements Comparator {
		private Map  _data = null;
		public ValueComparer (Map data){
			super();
			_data = data;
		}
		
         public int compare(Object o1, Object o2) {
        	 String e1 = (String) _data.get(o1);
             String e2 = (String) _data.get(o2);
             return e1.compareTo(e2);
         }
	}

	public static void main(String[] args){
		
		Map unsortedData = new HashMap();
		unsortedData.put("2", "DEF");
		unsortedData.put("1", "ABC");
		unsortedData.put("4", "ZXY");
		unsortedData.put("3", "BCD");
		
		
		SortedMap sortedData = new TreeMap(new MapValueSort.ValueComparer(unsortedData));
		
		printMap(unsortedData);
		
		sortedData.putAll(unsortedData);
		System.out.println();
		printMap(sortedData);
	}

	private static void printMap(Map data) {
		for (Iterator iter = data.keySet().iterator(); iter.hasNext();) {
			String key = (String) iter.next();
			System.out.println("Value/key:"+data.get(key)+"/"+key);
		}
	}
	
}

This should output something of the lines of:

Value/key:BCD/3
Value/key:DEF/2
Value/key:ZXY/4
Value/key:ABC/1

Value/key:ABC/1
Value/key:BCD/3
Value/key:DEF/2
Value/key:ZXY/4

Where the bottom last four lines obviously is sorted on the map’s values and not it’s keys.

24 Responses to Sorting maps in Java based on it’s values

  1. ravee says:

    Thank you and the code looks good, but its only working for values which are unique… but values may not be unique all the time.. can we do some thing to make it work for values which are repeating?

    Thanks
    Ravee

  2. Ray Pereda says:

    Replace the compare method with the following to make it work for values which are repeated:

    public int compare(Object key1, Object key2) {
    Comparable value1 = (Comparable) map.get(key1);
    Comparable value2 = (Comparable) map.get(key2);
    int c = value1.compareTo(value2);
    if (0 != c)
    return c;
    Integer h1 = key1.hashCode(), h2 = key2.hashCode();
    return h1.compareTo(h2);
    }

    You can’t add to the value sorted map after you create the TreeMap. It would be nicer if it worked just like TreeMap only it sorted on the values. I claim that 90% of the time, you want a sorted map sorted by value, not by key.

  3. Manju says:

    Excellent!!!!!!!!!!!11

  4. Pingback: Map nach value sortieren? - Java @ tutorials.de: Forum, Tutorial, Anleitung, Schulung & Hilfe

  5. Ken Weiner says:

    Here is a version of ValueComparer written with generics:

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

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

    public int compare(K key1, K key2) {
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value1.compareTo(value2);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }
    }

  6. Pingback: Java: Map sorted by value | Molindo Techblog

  7. jethroborsje says:

    This is a more complete version with generics:

       private static class ValueComparer<K, V extends Comparable> implements Comparator
       {
          private final Map map;
    
          public ValueComparer(Map map)
          {
             this.map = map;
          }
    
          public int compare(K key1, K key2)
          {
             V value1 = this.map.get(key1);
             V value2 = this.map.get(key2);
             int c = value1.compareTo(value2);
             if (c != 0)
             {
                return c;
             }
             Integer hashCode1 = key1.hashCode();
             Integer hashCode2 = key2.hashCode();
             return hashCode1.compareTo(hashCode2);
          }
       }
  8. Michael says:

    I had Eclipse warning me about “References to generic type Map should be parameterized” so I modified it a bit further to:

    import java.util.*;

    public class MapValueSort {

    /** inner class to do soring of the map **/
    private static class ValueComparer<K, V extends Comparable> implements
    Comparator {
    private final Map map;

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

    public int compare(K key1, K key2) {
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value1.compareTo(value2);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }

    }

    public static void main(String[] args) {

    Map unsortedData = new HashMap();
    unsortedData.put(“2”, “DEF”);
    unsortedData.put(“1”, “ABC”);
    unsortedData.put(“4”, “ZXY”);
    unsortedData.put(“3”, “BCD”);

    SortedMap sortedData = new TreeMap(
    new MapValueSort.ValueComparer(unsortedData));

    printMap(unsortedData);

    sortedData.putAll(unsortedData);
    System.out.println();
    printMap(sortedData);
    }

    private static void printMap(Map data) {
    for (Iterator iter = data.keySet().iterator(); iter.hasNext();) {
    String key = iter.next();
    System.out.println(“Value/key:” + data.get(key) + “/” + key);
    }
    }

    }

  9. Michael says:

    Now I understand a bit better, not specifing the types is what makes it interesting. I now use the following:

    import java.util.*;

    /** Sorting a Map by Value */
    public class MapValueSort {

    /** inner class to do soring of the map **/
    private static class ValueComparer<K, V extends Comparable> implements
    Comparator {
    private final Map map;

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

    /** Compare to values of a Map */
    public int compare(K key1, K key2) {
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value1.compareTo(value2);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }

    }

    /** Sorts a given Map according to its values and returns a sortedmap */
    public static SortedMap getValueSortedMap(
    Map map) {
    Comparator vc = new MapValueSort.ValueComparer(map);
    SortedMap sm = new TreeMap(vc);
    // add all Elements of unsorted Map, otherwise it is empty
    sm.putAll(map);
    return sm;
    }

    }

  10. Tom says:

    I was encountering some compilation errors, therefore I adapted it a little bit to sort Map objects. I also changed line “int c = value1.compareTo(value2);” to “int c = value2.compareTo(value1);” to sort in descending order.

    import java.util.*;

    /** Sorting a Map by Value */
    public class MapValueSort {

    /** inner class to do sorting of the map **/

    private static class ValueComparer implements Comparator {
    private final Map map;

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

    /** Compare to values of a Map */
    public int compare(Object key1, Object key2) {
    Double value1 = (Double) this.map.get(key1);
    Double value2 = (Double) this.map.get(key2);
    int c = value2.compareTo(value1);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }

    }

    /** Sorts a given Map according to its values and returns a sortedmap */
    public static SortedMap getValueSortedMap(Map map) {
    Comparator vc = new MapValueSort.ValueComparer(map);
    SortedMap sm = new TreeMap(vc);
    // add all Elements of unsorted Map, otherwise it is empty
    sm.putAll(map);
    return sm;
    }

    public static void main(String[] args) {
    TreeMap a = new TreeMap();
    a.put(“1OHR_AB_1A8X_A”, 80.81);
    a.put(“1OHR_AB_1A06_A”, 75.88);
    a.put(“1OHR_AB_1AMU_A”, 73.96);
    a.put(“1OHR_AB_1JPA_A”, 73.88);
    a.put(“1OHR_AB_1A6E_A”, 72.9);
    a.put(“1OHR_AB_1AOP_A”, 72.63);
    SortedMap b = getValueSortedMap(a);
    Iterator itr = b.entrySet().iterator();
    while (itr.hasNext()) {
    System.out.println(itr.next());
    }

    }
    }

  11. Tom says:

    The previous message is missing the text within ”, and hence the code is wrong!

  12. Pingback: Michael Rentzsch (mren) 's status on Monday, 13-Jul-09 13:43:39 UTC - Identi.ca

  13. Kiran says:

    The generics version of the ValueComparator is giving some compilation errors listed below. Please advice.

    @ ValueComparer inner class declaration
    The type MapValueSort.ValueComparer must implement the inherited abstract method
    Comparator.compare(Object, Object)

    @ compare method
    Name clash: The method compare(K, K) of type MapValueSort.ValueComparer has the
    same erasure as compare(T, T) of type Comparator but does not override it

  14. Olov Davidsson says:

    Try this version of the ValueComparer class, this did the trick for me:

    private static class ValueComparer<K, V extends Comparable> implements Comparator {
    private final Map map;

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

    // Compare two values in a map
    public int compare(K key1, K key2) {
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value1.compareTo(value2);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }
    }

  15. Olov Davidsson says:

    Hey, this web page is screwing up the code…
    Second try:

    private static class ValueComparer<K, V extends Comparable<V>> implements Comparator<K> {
    private final Map<K, V> map;

    public ValueComparer(Map<K, V> map) {
    this.map = map;
    }

    // Compare two values in a map (in descending order)
    public int compare(K key1, K key2) {
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value2.compareTo(value1);
    if (c != 0) {
    return c;
    }
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return hashCode1.compareTo(hashCode2);
    }
    }

  16. Rajhans says:

    Nice solution, this solution help me a lot to sort a map by value.

  17. Pingback: Unchecked call to compareTo - Question Lounge

  18. Pingback: Take 2: Sorting maps in Java based on it’s values « Observations

  19. Pingback: Java: Map sorted by value Molindo Techblog - Molindo Techblog – formerly known as talk-on-tech.blogspot.com

  20. Hadj says:

    //here I added ASC or DEC

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

    /** Sorting a Map by Value */
    public class MapValueSort{

    /** inner class to do sorting of the map **/

    private static class ValueComparer<K, V extends Comparable> implements Comparator {
    private final Map map;
    boolean ASC;

    public ValueComparer(Map map, boolean ASC) {
    this.map = map;
    this.ASC=ASC;
    }

    // Compare two values in a map
    public int compare(K key1, K key2) {
    int ascDec=1;
    if(!this.ASC) ascDec =-1;
    V value1 = this.map.get(key1);
    V value2 = this.map.get(key2);
    int c = value1.compareTo(value2);
    if (c != 0) {
    return c*ascDec;
    }
    // if the two values are equal we go by key hashcode
    Integer hashCode1 = key1.hashCode();
    Integer hashCode2 = key2.hashCode();
    return (hashCode1.compareTo(hashCode2))*ascDec;
    }
    }

    public static SortedMap getValueSortedMap(Map map,boolean ASC) {
    Comparator vc = new MapValueSort.ValueComparer(map,ASC);
    SortedMap sm = new TreeMap(vc);
    // add all Elements of unsorted Map, otherwise it is empty
    sm.putAll(map);
    return sm;
    }

    /*public static void main(String[] args) {
    TreeMap a = new TreeMap();
    a.put(“1”, 80.81);
    a.put(“3”, 73.96);
    a.put(“2”, 75.88);
    a.put(“52”, 72.9);
    a.put(“4”, 73.88);
    a.put(“6”, 72.63);
    a.put(“5”, 72.9);

    SortedMap b = getValueSortedMap(a,true);
    Iterator<Map.Entry> itr = b.entrySet().iterator();
    while (itr.hasNext()) {
    System.out.println(itr.next());
    }

    }*/

    }

  21. thushara says:

    Well, you won’t run into these issues of having to handle duplicates if a simple List is used for sorting. Insert Map.Entry objects from the Map in the List. Then sort the list with a comparator that compares the value (not the key) in the Map.Entry object.

    List<Map.Entry> list = new LinkedList<Map.Entry>();
    for (Map.Entry entry : wordMap.entrySet()) {
    list.add(entry);
    }
    Collections.sort(list, new Comparator<Map.Entry>() {
    public int compare(Map.Entry e1, Map.Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
    }
    });

    for (Map.Entry e : list) {
    System.out.println(e.getKey() + ” => ” + e.getValue());
    }

  22. Pingback: Automatically sorted by values map in Java

Leave a comment