En Java, podemos usar el TreeMap
class para ordenar un mapa por sus claves. Esta clase es muy útil de usar. Sin embargo, a veces necesitamos ordenar un mapa por sus valores. Cómo ordenar un mapa por sus valores es una de las preguntas más frecuentes de los programadores de Java. En esta publicación, desarrollaré la mejor manera de escribir dicho método.
1. Método ingenuo
La siguiente es una solución para ordenar un mapa de pares
import java.util.Comparator; import java.util.HashMap; import java.util.TreeMap; public class SortMapByValue { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("a", 10); map.put("b", 30); map.put("c", 50); map.put("d", 40); map.put("e", 20); System.out.println(map); TreeMap<String, Integer> sortedMap = sortMapByValue(map); System.out.println(sortedMap); } public static TreeMap<String, Integer> sortMapByValue(HashMap<String, Integer> map){ Comparator<String> comparator = new ValueComparator(map); //TreeMap is a map sorted by its keys. //The comparator is used to sort the TreeMap by keys. TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator); result.putAll(map); return result; } } |
Aquí está la implementación de la clase de comparador.
// a comparator that compares Strings class ValueComparator implements Comparator<String>{ HashMap<String, Integer> map = new HashMap<String, Integer>(); public ValueComparator(HashMap<String, Integer> map){ this.map.putAll(map); } @Override public int compare(String s1, String s2) { if(map.get(s1) >= map.get(s2)){ return -1; }else{ return 1; } } } |
En esta solución, usamos un TreeMap para ordenar el mapa. Al crear el TreeMap, le damos un comparador. El comparador acepta cadenas y compara los valores asociados de la clave de cadena dada en el mapa.
El método funciona bien, pero solo funciona para ordenar pares de cadenas y enteros. Si queremos ordenar un mapa con otros tipos de claves y valores, es necesario reescribirlo. Por tanto, se prefiere una solución más general.
2. Solución más general
Podemos ignorar el tipo genérico y hacer que los métodos funcionen para cualquier tipo como el siguiente.
import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; public class Solution { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("a", 10); map.put("b", 30); map.put("c", 50); map.put("d", 40); map.put("e", 20); System.out.println(map); Map sortedMap = sortByValue(map); System.out.println(sortedMap); } public static Map sortByValue(Map unsortedMap) { Map sortedMap = new TreeMap(new ValueComparator(unsortedMap)); sortedMap.putAll(unsortedMap); return sortedMap; } } class ValueComparator implements Comparator { Map map; public ValueComparator(Map map) { this.map = map; } public int compare(Object keyA, Object keyB) { Comparable valueA = (Comparable) map.get(keyA); Comparable valueB = (Comparable) map.get(keyB); return valueB.compareTo(valueA); } } |
La solución no es de tipo seguro, necesitamos una solución general y de tipo seguro.
3. Uso de tipos genéricos
public class SortMapByValue { public static void main(String[] args) { // <String, Integer> Map HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("a", 10); map.put("b", 30); map.put("c", 50); map.put("d", 40); map.put("e", 20); System.out.println(map); Comparator<String> comparator = new ValueComparator<String, Integer>(map); TreeMap<String, Integer> result = new TreeMap<String, Integer>(comparator); result.putAll(map); System.out.println(result); // <Integer, Integer> Map HashMap<Integer, Integer> map2 = new HashMap<Integer, Integer>(); map2.put(1, 10); map2.put(2, 30); map2.put(3, 50); map2.put(4, 40); map2.put(5, 20); System.out.println(map2); Comparator<Integer> comparator2 = new ValueComparator<Integer, Integer>(map2); TreeMap<Integer, Integer> result2 = new TreeMap<Integer, Integer>(comparator2); result2.putAll(map2); System.out.println(result2); } } |
// a comparator using generic type class ValueComparator<K, V extends Comparable<V>> implements Comparator<K>{ HashMap<K, V> map = new HashMap<K, V>(); public ValueComparator(HashMap<K, V> map){ this.map.putAll(map); } @Override public int compare(K s1, K s2) { return -map.get(s1).compareTo(map.get(s2));//descending order } } |
4. Otra forma de utilizar tipos genéricos
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) { List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet()); Collections.sort(list, new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { return (e1.getValue()).compareTo(e2.getValue()); } }); Map<K, V> result = new LinkedHashMap<>(); for (Map.Entry<K, V> entry : list) { result.put(entry.getKey(), entry.getValue()); } return result; } |
5. Simple
Cuando necesite una solución simple, la clasificación se puede hacer en una línea.
LinkedList<Map.Entry<String, Integer>> list = new LinkedList<>(counter.entrySet()); Comparator<Map.Entry<String, Integer>> comparator = Comparator.comparing(Map.Entry::getValue); Collections.sort(list, comparator.reversed()); |