36

Possible Duplicate:
How to sort a Map<Key, Value> on the values in Java?

I am using map interface to read from a file and then store the values in that as a key value pair. The file format is as follows

 A 34
 B 25
 c 50

I will read the datas from this file and store that as a key value pair and then I will display this to the user. My requirement is to display the results in this format

C 50
A 34
B 25

Thus I need to sort the map in descending order of the value. So that I will be able to display these as my result .. I have read about this and find the below code

static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>> entriesSortedByValues(Map<K,V> map) {
        SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
            new Comparator<Map.Entry<K,V>>() {
                @Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
                    int res = e1.getValue().compareTo(e2.getValue());
                    return res != 0 ? res : 1; // Special fix to preserve items with equal values
                }
            }
        );
        sortedEntries.addAll(map.entrySet());
        return sortedEntries;
    }

I hope this is gonna sort the values in ascending order, I just want to know whether this approach is correct or some other effective approach will be helpful for me ?

Community
  • 1
  • 1
NandaKumar
  • 905
  • 4
  • 15
  • 19
  • Perhaps you should take a look at Guava. See [this](http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java/3420912#3420912) comment. – Andrew Logvinov Jul 25 '12 at 10:44
  • @NandaKumar: `Set`s doesn't allow duplicates, use for example a `List`. You break the contract of `Comparator` if you start allowing them! – dacwe Jul 25 '12 at 11:08
  • ` map.entrySet().stream().sorted(Comparator.comparing(e->-e.getValue())).forEach(System.out::println);` – manoj jangam Aug 05 '17 at 12:17

2 Answers2

55

Since you can have duplicate values you shouldn't be using a Set at all. Change to a List and sort it instead. Your entriesSortedByValues would look something like this:

static <K,V extends Comparable<? super V>> 
            List<Entry<K, V>> entriesSortedByValues(Map<K,V> map) {

    List<Entry<K,V>> sortedEntries = new ArrayList<Entry<K,V>>(map.entrySet());

    Collections.sort(sortedEntries, 
            new Comparator<Entry<K,V>>() {
                @Override
                public int compare(Entry<K,V> e1, Entry<K,V> e2) {
                    return e2.getValue().compareTo(e1.getValue());
                }
            }
    );

    return sortedEntries;
}

Note: in your example output the values is descending. If you want them ascending, use e1.getValue().compareTo(e2.getValue()) instead.


Example:

public static void main(String args[]) {

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("A", 34);
    map.put("B", 25);
    map.put("C", 50);
    map.put("D", 50); // "duplicate" value

    System.out.println(entriesSortedByValues(map));
}

Output:

[D=50, C=50, A=34, B=25]
dacwe
  • 43,066
  • 12
  • 116
  • 140
14

Write your own comparator and pass it to TreeMap

class MyComparator implements Comparator {

Map map;

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

public int compare(Object o1, Object o2) {

    return ((Integer) map.get(o2)).compareTo((Integer) map.get(o1));

}
}

In Test Class

Map<String, Integer> lMap=new HashMap<String, Integer>();
    lMap.put("A", 35);
    lMap.put("B", 25);
    lMap.put("C", 50);

    MyComparator comp=new MyComparator(lMap);

    Map<String,Integer> newMap = new TreeMap(comp);
    newMap.putAll(lMap);

OutPut:

C=50
A=35
B=25
amicngh
  • 7,831
  • 3
  • 35
  • 54
  • 2
    This doesn't work if you add `lMap.put("D", 50)` since it would be considered as a duplicate (and would in fact override any other value with `50` e.g. `"C"`). – dacwe Jul 25 '12 at 10:56
  • 1
    duplicate value is not allowed . – amicngh Jul 25 '12 at 10:58
  • 1
    @amicngh: Thank you for your kind solution. But,I want the duplicate value to be allowed . What would be the possible way to get the same. Thank you. – NandaKumar Jul 25 '12 at 11:01
  • The following code will work. public Map sortByValue(Map map) { List list = new LinkedList(map.entrySet()); Collections.sort(list, new Comparator() { public int compare(Object o2, Object o1) { return ((Comparable) ((Map.Entry) (o1)).getValue()) .compareTo(((Map.Entry) (o2)).getValue()); } }); Map result = new LinkedHashMap(); for (Iterator it = list.iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); result.put(entry.getKey(), entry.getValue()); } return result; } – Learner Aug 26 '15 at 11:29
  • 1
    You should use new TreeMap(Collections.reverseOrder());. Map newMap = new TreeMap(Collections.reverseOrder()); newMap.putAll(myMap); – Tell Me How Jan 18 '17 at 04:50
  • @NandaKumar you could add an if clause in the compareTo method on what to do if both the values are same. For e.g , here I'm ordering by keys in descending order if two values are same. public int compare(Object o1, Object o2) { if(map.get(o2) == map.get(o1)) return o2.toString().compareTo(o1.toString()); return ((Integer) map.get(o2)).compareTo((Integer) map.get(o1)); – acacia Mar 25 '21 at 09:45