¿Cómo ordenar los valores del Mapa por clave en Java?

362

Tengo un mapa que tiene cadenas para claves y valores.

Los datos son como los siguientes:

"pregunta1", "1"
"pregunta9", "1"
"pregunta2", "4"
"pregunta5", "2"

Quiero ordenar el mapa en función de sus claves. Entonces, al final, tendré question1, question2, question3... y así sucesivamente.


Eventualmente, estoy tratando de sacar dos cadenas de este Mapa.

  • Primera cadena: preguntas (en orden 1 ..10)
  • Segunda cadena: respuestas (en el mismo orden que la pregunta)

En este momento tengo lo siguiente:

Iterator it = paramMap.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry) it.next();
    questionAnswers += pairs.getKey() + ",";
}

Esto me lleva a las preguntas en una cadena pero no están en orden.

n00bstackie
fuente

Respuestas:

613

Respuesta corta

Use a TreeMap. Esto es precisamente para lo que sirve.

Si se le pasa este mapa y no puede determinar el tipo, puede hacer lo siguiente:

SortedSet<String> keys = new TreeSet<>(map.keySet());
for (String key : keys) { 
   String value = map.get(key);
   // do something
}

Esto iterará a través del mapa en el orden natural de las teclas.


Respuesta más larga

Técnicamente, puede usar cualquier cosa que implemente SortedMap, pero excepto en casos excepcionales, esto equivale a TreeMap, al igual que el uso de una Mapimplementación HashMap.

Para los casos en que sus claves son de un tipo complejo que no implementa Comparable o si no desea utilizar el orden natural en ese momento TreeMapy TreeSettiene constructores adicionales que le permiten pasar a Comparator:

// placed inline for the demonstration, but doesn't have to be a lambda expression
Comparator<Foo> comparator = (Foo o1, Foo o2) -> {
        ...
    }

SortedSet<Foo> keys = new TreeSet<>(comparator);
keys.addAll(map.keySet());

Recuerde cuando use un TreeMapo TreeSetque tendrá características de rendimiento diferentes que HashMapo HashSet. En términos generales, las operaciones que encuentran o insertan un elemento irán de O (1) a O (Log (N)) .

En un caso HashMap, pasar de 1000 artículos a 10,000 realmente no afecta su tiempo para buscar un elemento, pero para un TreeMaptiempo de búsqueda será aproximadamente 3 veces más lento (suponiendo el Registro 2 ). Pasar de 1000 a 100,000 será aproximadamente 6 veces más lento para cada búsqueda de elementos.

Jherico
fuente
Estoy tratando de usar Treemap y ordenar las claves de cadena según la longitud. Me parece que estoy obteniendo resultados de recuperación inconsistentes. Aparentemente porque TreeMap considera un resultado compareTo de 0 como "igual"? No estoy seguro de cómo usarlo en este caso.
Marc
1
El resultado compareTo () de 0 es 'igual'. Si está escribiendo un comparador que ordena por longitud de cadena, entonces necesita devolver un valor positivo o negativo en función de qué cadena es más larga, y solo devolver 0 si ambas cadenas tienen la misma longitud. si a y b son cadenas, puede hacer esto así: `return a.length () - b.length () '(o invierta los valores si desea ordenarlos en la otra dirección).
Jherico
Hola chicos, si él / ella iría al Mapa para ser ordenado por claves, que aquí es 1,2,3,4, ¿cuál es el orden de inserción ... ¿por qué no estamos usando LinkedHashSet? Simplemente hacemos las preguntas una por una, y se ordena por el orden de la inserción. ¿Puede alguien ayudarme con esto?
Karoly
@Karoly LinkedHashSet funcionaría para recuperar los elementos en el orden en que los insertó. Lo que quiere el OP es recuperar los elementos en un orden de clasificación predeterminado, independientemente del orden de inserción.
David Berry
@ cricket_007 el código está demostrando específicamente cómo iterar a través de las teclas para un mapa que aún no está ordenado.
Jherico
137

Suponiendo que TreeMap no es bueno para usted (y suponiendo que no puede usar genéricos):

List sortedKeys=new ArrayList(yourMap.keySet());
Collections.sort(sortedKeys);
// Do what you need with sortedKeys.
TrayMan
fuente
3
¡Gracias! Necesitaba hacer algo como esto ya que mis claves eran de un tipo complejo.
Ross Hambrick
3
Esto solo ordenará la lista de claves, pero no clasificará el mapa en sí mismo en función de las claves. También estoy buscando cómo ordenar el mapa en función de las teclas y podría encontrar una manera. Tendré que probar con TreeMap, supongo :)
Crenguta S
55

Usando el TreeMappuede ordenar el mapa.

Map<String, String> map = new HashMap<>();        
Map<String, String> treeMap = new TreeMap<>(map);
for (String str : treeMap.keySet()) {
    System.out.println(str);
}
Manoj Singh
fuente
1
Map <String, List <String>> treeMap = new TreeMap <String, List <String>> (printHashMap); for (String str: treeMap.keySet ()) {System.out.println (str + "" + treeMap.get (str)); }
vikramvi
37

¡Usa un TreeMap !

AgileJon
fuente
31
+1 - No es lo suficientemente ágil como para vencer a Jherico, Jon, pero sigue siendo bastante bueno. 8)
duffymo
No sé Java :-( Esto funciona al 100%. Mucho más simple que cualquiera de las soluciones horribles que se me ocurrió
Peterter
36

Si ya tiene un mapa y desea ordenarlo por teclas, simplemente use:

Map<String, String> treeMap = new TreeMap<String, String>(yourMap);

Un ejemplo de trabajo completo:

import java.util.HashMap;
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.Iterator;

class SortOnKey {

public static void main(String[] args) {
   HashMap<String,String> hm = new HashMap<String,String>();
   hm.put("3","three");
   hm.put("1","one");
   hm.put("4","four");
   hm.put("2","two");
   printMap(hm);
   Map<String, String> treeMap = new TreeMap<String, String>(hm);
   printMap(treeMap);
}//main

public static void printMap(Map<String,String> map) {
    Set s = map.entrySet();
    Iterator it = s.iterator();
    while ( it.hasNext() ) {
       Map.Entry entry = (Map.Entry) it.next();
       String key = (String) entry.getKey();
       String value = (String) entry.getValue();
       System.out.println(key + " => " + value);
    }//while
    System.out.println("========================");
}//printMap

}//class
Maryland
fuente
36

Solo usa TreeMap

new TreeMap<String, String>(unsortMap);

Tenga en cuenta que el TreeMap está ordenado de acuerdo con el orden natural de sus 'claves'

Aliti
fuente
19

Siempre que no pueda usar TreeMap, en Java 8 podemos usar el método toMap () en el Collectorsque toma los siguientes parámetros:

  • keymapper : función de mapeo para producir claves
  • valuemapper : función de mapeo para producir valores
  • mergeFunction : una función de combinación, utilizada para resolver colisiones entre valores asociados con la misma clave
  • mapSupplier : una función que devuelve un nuevo mapa vacío en el que se insertarán los resultados.

Ejemplo de Java 8

Map<String,String> sample = new HashMap<>();  // push some values to map  
Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                    .sorted(Map.Entry.<String,String>comparingByKey().reversed())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
Map<String, String> newMapSortedByValue = sample.entrySet().stream()
                        .sorted(Map.Entry.<String,String>comparingByValue().reversed())
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));

Podemos modificar el ejemplo para usar un comparador personalizado y ordenar según las claves como:

Map<String, String> newMapSortedByKey = sample.entrySet().stream()
                .sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1,e2) -> e1, LinkedHashMap::new));
akhil_mittal
fuente
solo funciona en Android con Api 24 y superior.
Tina
10

Usando Java 8:

Map<String, Integer> sortedMap = unsortMap.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
Taras Melnyk
fuente
5

Este código puede ordenar un mapa clave-valor en ambos órdenes, es decir, ascendente y descendente.

<K, V extends Comparable<V>> Map<K, V> sortByValues
     (final Map<K, V> map, int ascending)
{
     Comparator<K> valueComparator =  new Comparator<K>() {         
        private int ascending;
        public int compare(K k1, K k2) {
            int compare = map.get(k2).compareTo(map.get(k1));
            if (compare == 0) return 1;
            else return ascending*compare;
        }
        public Comparator<K> setParam(int ascending)
        {
            this.ascending = ascending;
            return this;
        }
    }.setParam(ascending);

    Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
    sortedByValues.putAll(map);
    return sortedByValues;
}

Como ejemplo:

Map<Integer,Double> recommWarrVals = new HashMap<Integer,Double>();
recommWarrVals = sortByValues(recommWarrVals, 1);  // Ascending order
recommWarrVals = sortByValues(recommWarrVals,-1);  // Descending order
M. Mashaye
fuente
5

En Java 8

Para ordenar una Map<K, V>por clave, colocando claves en un List<K>:

List<K> result = map.keySet().stream().sorted().collect(Collectors.toList());

Para ordenar una Map<K, V>por clave, colocando entradas en un List<Map.Entry<K, V>>:

List<Map.Entry<K, V>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey())
       .collect(Collectors.toList());

Por último, pero no menos importante: para ordenar las cadenas de manera sensible a la configuración regional, use una clase Collator (comparador):

Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.PRIMARY); // case insensitive collator

List<Map.Entry<String, String>> result =
    map.entrySet()
       .stream()
       .sorted(Map.Entry.comparingByKey(collator))
       .collect(Collectors.toList());
Oleksandr Pyrohov
fuente
1
List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
for (String str : map.keySet()) {
 list.add(str);
}
Collections.sort(list);
for (String str : list) {
 System.out.println(str);
}
Manoj Singh
fuente
1

En Java 8 también puede usar .stream (). Sorted ():

myMap.keySet().stream().sorted().forEach(key -> {
        String value = myMap.get(key);

        System.out.println("key: " + key);
        System.out.println("value: " + value);
    }
);
Jonas_Hess
fuente
0

También podemos ordenar la clave utilizando el método Arrays.sort.

Map<String, String> map = new HashMap<String, String>();
Object[] objArr = new Object[map.size()];
for (int i = 0; i < map.size(); i++) {
objArr[i] = map.get(i);
}
Arrays.sort(objArr);
for (Object str : objArr) {
System.out.println(str);
}
Manoj Singh
fuente
Esto se ordena por valor y no por clave.
raaj
0

Por si acaso no quieres usar un TreeMap

public static Map<Integer, Integer> sortByKey(Map<Integer, Integer> map) {
    List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
    list.sort(Comparator.comparingInt(Map.Entry::getKey));
    Map<Integer, Integer> sortedMap = new HashMap<>();
    list.forEach(sortedMap.put(e.getKey(), e.getValue()));
    return sortedMap;
}

Además, en caso de que desee ordenar su mapa en función de valuessolo cambiar Map.Entry::getKeyaMap.Entry::getValue

Ankit Sharma
fuente
Esto apareció en mi registro de revisión. Alguien intentó corregir algunos errores con nombres de variables (pero falló). Entonces, los arreglé correctamente, sin embargo, esta respuesta es básicamente incorrecta. El orden en el que agrega valores a un HashMap es irrelevante. Los mapas no tienen orden. stackoverflow.com/questions/10710193/…
aepryus