Take 2: Sorting maps in Java based on it’s values


In my old post Sorting maps in Java based on it’s values I wrote about a simple way to sort a Map object in Java based on the Map values, not the keys. That was back in good old Java pre-generics days. So how can you do the same with Java generics?

After a little fiddeling, here is how I ended up doing it:


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

public class MapValueSort {

    public static void main(String[] args) {

        Map<String, String> unsortedMap = new HashMap<String, String>();
        unsortedMap.put("2", "DEF");
        unsortedMap.put("3", "DEF");
        unsortedMap.put("1", "ABC");
        unsortedMap.put("5", "ZXY");
        unsortedMap.put("4", "BCD");

        printMap(unsortedMap);

        Map sortedMap = MapValueSort.sortMapByValue(unsortedMap);
       
        System.out.println();

        printMap(sortedMap);
    }

    /*
     * Util method that:
     * 1. Constructs a sorted map object providing a Comparator class
     * 2. Populating the sorted map by calling putAll with the UNsorted 
     *     data.
     * 3. When the putAll is called, the comparator is used to do the
     *     ordering.
     * 4. The method returns the sorted map.
     */
    static private <K, V extends Comparable<V>> Map<K, V> sortMapByValue(Map<K, V> unsortedMap) {
        SortedMap<K, V> sortedMap = new TreeMap<K, V>(new ValueComparer<K, V>(unsortedMap) );
        sortedMap.putAll(unsortedMap);
        return sortedMap;
    }

    /*
     * An inner class that implements a Comparator to compare the values inside the map.
     * Constructor takes the contentes of the UNsorted map.
     */
    private static class ValueComparer<K, V extends Comparable<V>> implements Comparator<K> {

        private final Map<K, V> map;

        public ValueComparer(Map<K, V> map) {
            super();
            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);
        }
    }

    /*
     * Utility method to print a map using an Iterator.
     */
    private static void printMap(Map<?,?> data) {
        for (Iterator iter = data.keySet().iterator(); iter.hasNext();) {
            Object key = iter.next();
            System.out.println("Value/key:" + data.get(key).toString() + "/" + key.toString());
        }
    }
}

Advertisements

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.