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