Sorting maps in Java based on it’s values
January 24, 2006 24 Comments
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.
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
Indeed code looks code but it needs to be udpated to use Generics. Here is another way of sorting HashMap in Java by keys which uses generics
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.
Excellent!!!!!!!!!!!11
Pingback: Map nach value sortieren? - Java @ tutorials.de: Forum, Tutorial, Anleitung, Schulung & Hilfe
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);
}
}
Pingback: Java: Map sorted by value | Molindo Techblog
This is a more complete version with generics:
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);
}
}
}
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;
}
}
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());
}
}
}
The previous message is missing the text within ”, and hence the code is wrong!
Pingback: Michael Rentzsch (mren) 's status on Monday, 13-Jul-09 13:43:39 UTC - Identi.ca
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
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);
}
}
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);
}
}
Nice solution, this solution help me a lot to sort a map by value.
Pingback: Unchecked call to compareTo - Question Lounge
Pingback: Take 2: Sorting maps in Java based on it’s values « Observations
Pingback: Java: Map sorted by value Molindo Techblog - Molindo Techblog – formerly known as talk-on-tech.blogspot.com
//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());
}
}*/
}
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());
}
really good one, i think that was smart, too many different ways to do the same thing,
Pingback: Automatically sorted by values map in Java