Soy relativamente nuevo en Java y, a menudo, encuentro que necesito ordenar un Map<Key, Value>
valor.
Dado que los valores no son únicos, me encuentro convirtiendo el keySet
en un array
, y ordenando esa matriz a través de la ordenación de matriz con un comparador personalizado que clasifica el valor asociado con la clave.
hay una manera mas facil?
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
yCollections.sort ....
así.Respuestas:
Aquí hay una versión genérica amigable:
fuente
forEachOrdered
lugar deforEach
, ya que los documentos deforEach
estados: "El comportamiento de esta operación es explícitamente no determinista"?Nota IMPORTANTE:
Este código puede romperse de múltiples maneras. Si tiene la intención de utilizar el código proporcionado, asegúrese de leer los comentarios también para estar al tanto de las implicaciones. Por ejemplo, los valores ya no pueden ser recuperados por su clave. (
get
siempre regresanull
)Parece mucho más fácil que todo lo anterior. Use un TreeMap de la siguiente manera:
Salida:
fuente
return ((Comparable)base.get(a).compareTo(((Comparable)base.get(b)))
?map.put("A","1d");map.put("B","1d");map.put("C",67d);map.put("D",99.5d);
Java 8 ofrece una nueva respuesta: convierta las entradas en una secuencia y use los combinadores de comparación de Map.Entry:
Esto le permitirá consumir las entradas ordenadas en orden ascendente de valor. Si desea un valor descendente, simplemente invierta el comparador:
Si los valores no son comparables, puede pasar un comparador explícito:
Luego puede proceder a utilizar otras operaciones de flujo para consumir los datos. Por ejemplo, si desea los 10 mejores en un nuevo mapa:
O imprima a
System.out
:fuente
parallelStream()
en este caso?Tres respuestas de 1 línea ...
Me gustaría utilizar
Google Coleccionesde guayaba para hacer esto - si sus valores sonComparable
entonces usted puede utilizarLo cual creará una función (objeto) para el mapa [que toma cualquiera de las teclas como entrada, devolviendo el valor respectivo], y luego les aplica un orden natural (comparable) [los valores].
Si no son comparables, entonces deberá hacer algo similar a
Estos pueden aplicarse a un TreeMap (como se
Ordering
extiendeComparator
), o un LinkedHashMap después de ordenarlosNB : Si va a utilizar un TreeMap, recuerde que si se compara == 0, el elemento ya está en la lista (lo que sucederá si tiene varios valores que comparan lo mismo). Para aliviar esto, puede agregar su clave al comparador de esta manera (suponiendo que sus claves y valores sean
Comparable
):= Aplicar ordenamiento natural al valor mapeado por la clave, y combinarlo con el ordenamiento natural de la clave
Tenga en cuenta que esto aún no funcionará si sus claves se comparan con 0, pero esto debería ser suficiente para la mayoría de los
comparable
elementos (comohashCode
,equals
y acompareTo
menudo están sincronizados ...)Consulte Ordering.onResultOf () y Functions.forMap () .
Implementación
Entonces, ahora que tenemos un comparador que hace lo que queremos, necesitamos obtener un resultado.
Ahora esto probablemente funcionará, pero:
TreeMap
; no tiene sentido intentar comparar una clave insertada cuando no tiene un valor hasta después de la colocación, es decir, se romperá realmente rápidoEl punto 1 es un poco decisivo para mí; Las colecciones de Google son increíblemente perezosas (lo cual es bueno: puedes hacer casi todas las operaciones en un instante; el trabajo real se hace cuando comienzas a usar el resultado), ¡y esto requiere copiar un mapa completo !
Respuesta "completa" / Mapa ordenado en vivo por valores
Sin embargo, no te preocupes; Si estaba lo suficientemente obsesionado con tener un mapa "en vivo" ordenado de esta manera, podría resolver no uno, sino ambos (!) de los problemas anteriores con algo loco como el siguiente:
Nota: Esto ha cambiado significativamente en junio de 2012: el código anterior nunca podría funcionar: se necesita un HashMap interno para buscar los valores sin crear un bucle infinito entre
TreeMap.get()
->compare()
ycompare()
->get()
Cuando lo ponemos, nos aseguramos de que el mapa hash tenga el valor para el comparador, y luego lo colocamos en el TreeSet para ordenarlo. Pero antes de eso, verificamos el mapa hash para ver que la clave no es realmente un duplicado. Además, el comparador que creamos también incluirá la clave para que los valores duplicados no eliminen las claves no duplicadas (debido a == comparación). Estos 2 elementos son vitales para garantizar que se mantenga el contrato del mapa; si crees que no quieres eso, entonces estás casi a punto de invertir el mapa por completo (a
Map<V,K>
).El constructor necesitaría ser llamado como
fuente
Ordering
Es simplemente un ricoComparator
. He intentado comentar cada ejemplo (las cursivas debajo de cada uno). "natural" indica que los objetos sonComparable
; es como el ComparableComparator de apache common.onResultOf
aplica una función al elemento que se compara. Entonces, si tuviera una función que agregara 1 a un entero,natural().onResultOf(add1Function).compare(1,2)
terminaría haciendo2.compareTo(3)
ImmutableSetMultiMap
oImmutableListMultiMap
contener la colección de variables duplicadas.Desde http://www.programmersheaven.com/download/49349/download.aspx
fuente
Con Java 8, puede usar la API de secuencias para hacerlo de una manera significativamente menos detallada:
fuente
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
Ordenar las claves requiere que el Comparador busque cada valor para cada comparación. Una solución más escalable usaría el entrySet directamente, ya que el valor estaría disponible de inmediato para cada comparación (aunque no he respaldado esto por números).
Aquí hay una versión genérica de tal cosa:
Hay formas de disminuir la rotación de memoria para la solución anterior. La primera ArrayList creada podría, por ejemplo, reutilizarse como valor de retorno; esto requeriría la supresión de algunas advertencias genéricas, pero podría valer la pena para el código de biblioteca reutilizable. Además, el Comparador no tiene que ser reasignado en cada invocación.
Aquí hay una versión más eficiente, aunque menos atractiva:
Finalmente, si necesita acceder continuamente a la información ordenada (en lugar de solo ordenarla de vez en cuando), puede usar un mapa múltiple adicional. Déjeme saber si usted necesita más detalles...
fuente
La biblioteca commons-collections contiene una solución llamada TreeBidiMap . O bien, puede echar un vistazo a la API de Google Collections. Tiene TreeMultimap que puedes usar.
Y si no desea utilizar este marco ... vienen con código fuente.
fuente
He examinado las respuestas dadas, pero muchas de ellas son más complicadas de lo necesario o eliminan elementos del mapa cuando varias claves tienen el mismo valor.
Aquí hay una solución que creo que encaja mejor:
Tenga en cuenta que el mapa está ordenado del valor más alto al más bajo.
fuente
Para lograr esto con las nuevas características en Java 8:
Las entradas se ordenan por sus valores utilizando el comparador dado. Alternativamente, si sus valores son mutuamente comparables, no se necesita un comparador explícito:
La lista devuelta es una instantánea del mapa dado en el momento en que se llama a este método, por lo que ninguno reflejará los cambios posteriores al otro. Para una vista en vivo iterable del mapa:
El iterable devuelto crea una instantánea nueva del mapa dado cada vez que se repite, por lo que, salvo modificaciones simultáneas, siempre reflejará el estado actual del mapa.
fuente
Cree un comparador personalizado y úselo mientras crea un nuevo objeto TreeMap.
Use el siguiente código en su función principal
Salida:
fuente
Si bien estoy de acuerdo en que la necesidad constante de ordenar un mapa es probablemente un olor, creo que el siguiente código es la forma más fácil de hacerlo sin usar una estructura de datos diferente.
}
Y aquí hay una prueba unitaria vergonzosamente incompleta:
}
El resultado es una lista ordenada de objetos Map.Entry, de la que puede obtener las claves y los valores.
fuente
Use un comparador genérico como:
fuente
La respuesta más votada no funciona cuando tienes 2 ítems iguales. TreeMap deja valores iguales.
el ejemplo: mapa sin clasificar
resultados
Así que deja de lado E !!
Para mí funcionó bien para ajustar el comparador, si es igual no devuelve 0 sino -1.
en el ejemplo:
ahora vuelve:
mapa sin clasificar:
resultados:
como respuesta a Aliens (22 de noviembre de 2011): Estoy usando esta solución para un mapa de Id. y nombres de enteros, pero la idea es la misma, por lo que podría ser que el código anterior no sea correcto (lo escribiré en una prueba y darle el código correcto), este es el código para una clasificación de Mapa, basado en la solución anterior:
y esta es la clase de prueba (acabo de probarla, y esto funciona para Integer, String Map:
Aquí está el código para el comparador de un mapa:
y este es el caso de prueba para esto:
por supuesto, puede hacer que esto sea mucho más genérico, pero solo lo necesitaba para 1 caso (el Mapa)
fuente
En lugar de usar
Collections.sort
como algunos sugiero usarArrays.sort
. En realidad, lo queCollections.sort
hace es algo como esto:Simplemente llama
toArray
a la lista y luego usaArrays.sort
. De esta manera, todas las entradas del mapa se copiarán tres veces: una vez del mapa a la lista temporal (ya sea LinkedList o ArrayList), luego a la matriz temporal y finalmente al nuevo mapa.Mi solución omite este paso ya que no crea LinkedList innecesaria. Aquí está el código, genérico y de rendimiento óptimo:
fuente
Esta es una variación de la respuesta de Anthony, que no funciona si hay valores duplicados:
Tenga en cuenta que está bastante en el aire cómo manejar nulos.
Una ventaja importante de este enfoque es que en realidad devuelve un Mapa, a diferencia de algunas de las otras soluciones que se ofrecen aquí.
fuente
Mejor enfoque
Salida
fuente
Problema importante. Si usa la primera respuesta (Google lo lleva aquí), cambie el comparador para agregar una cláusula igual; de lo contrario, no podrá obtener valores del sorted_map por teclas:
fuente
Ya hay muchas respuestas para esta pregunta, pero ninguna me proporcionó lo que estaba buscando, una implementación de mapa que devuelve claves y entradas ordenadas por el valor asociado, y mantiene esta propiedad a medida que las claves y los valores se modifican en el mapa. Otras dos preguntas piden esto específicamente.
Preparé un ejemplo amistoso genérico que resuelve este caso de uso. Esta implementación no cumple con todos los contratos de la interfaz Map, como reflejar los cambios de valor y las eliminaciones en los conjuntos devueltos por keySet () y entrySet () en el objeto original. Sentí que tal solución sería demasiado grande para incluirla en una respuesta de desbordamiento de pila. Si logro crear una implementación más completa, quizás la publique en Github y luego la enlace en una versión actualizada de esta respuesta.
fuente
Entrada tardía.
Con el advenimiento de Java-8, podemos usar flujos para la manipulación de datos de una manera muy fácil y sucinta. Puede usar secuencias para ordenar las entradas del mapa por valor y crear un LinkedHashMap que conserve la iteración del orden de inserción .
P.ej:
Para ordenar en reversa, reemplace:
con
fuente
Entry.comparingByValue()
(como la respuesta de asylias anterior stackoverflow.com/a/22132422/1480587 ) o lacomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
que usó? Entiendo que también compara claves si los valores son idénticos, ¿verdad? Noté que la ordenación mantiene el orden de los elementos con el mismo valor, por lo que ¿es necesaria la ordenación por claves si las claves ya están ordenadas antes?Mapa dado
Ordenar el mapa en función del valor en orden ascendente
Ordenar el mapa según el valor en orden de desecho
Salida:
{software = 50, tecnología = 70, EE. UU. = 100, trabajos = 200, oportunidad = 200}
{empleos = 200, oportunidad = 200, EE. UU. = 100, tecnología = 70, software = 50}
fuente
Dependiendo del contexto, usando
java.util.LinkedHashMap<T>
qué recuerda el orden en que se colocan los elementos en el mapa. De lo contrario, si necesita ordenar los valores en función de su orden natural, recomendaría mantener una lista separada que se pueda ordenar a través deCollections.sort()
.fuente
Como TreeMap <> no funciona para valores que pueden ser iguales, utilicé esto:
Es posible que desee poner la lista en un LinkedHashMap , pero si solo va a iterar sobre él de inmediato, es superfluo ...
fuente
Esto es demasiado complicado. Se suponía que los mapas no harían tal trabajo como ordenarlos por valor. La forma más fácil es crear su propia Clase para que se ajuste a sus necesidades.
En el ejemplo inferior, se supone que debe agregar a TreeMap un comparador en el lugar donde está *. Pero mediante la API de Java solo proporciona claves de comparación, no valores. Todos los ejemplos indicados aquí se basan en 2 mapas. Un hash y un nuevo árbol. Lo cual es extraño.
El ejemplo:
Así que cambie el mapa a un conjunto de esta manera:
Crearás clase
Results
,y la clase Comparator:
De esta manera, puede agregar fácilmente más dependencias.
Y como último punto agregaré un iterador simple:
fuente
Basado en el código @devinmoore, un método de clasificación de mapas que utiliza genéricos y admite el orden ascendente y descendente.
fuente
Aquí hay una solución OO (es decir, no utiliza
static
métodos):Por la presente donado al dominio público.
fuente
Afaik, la forma más limpia es utilizando colecciones para ordenar el mapa según el valor:
fuente
Algunos cambios simples para tener un mapa ordenado con pares que tengan valores duplicados. En el método de comparación (clase ValueComparator) cuando los valores son iguales, no devuelve 0 sino que devuelve el resultado de comparar las 2 claves. Las claves son distintas en un mapa, por lo que puede mantener valores duplicados (que por cierto se ordenan por claves). Entonces, el ejemplo anterior podría modificarse así:
fuente
Seguro que la solución de Stephen es realmente genial, pero para aquellos que no pueden usar Guava:
Aquí está mi solución para ordenar por valor un mapa. Esta solución maneja el caso donde hay dos veces el mismo valor, etc.
El ejecutivo: http://www.ideone.com/dq3Lu
La salida:
Espero que ayude a algunas personas
fuente
Si tiene claves duplicadas y solo un pequeño conjunto de datos (<1000) y su código no es crítico para el rendimiento, puede hacer lo siguiente:
inputUnsortedMap es la entrada al código.
La variable sortedOutputMap contendrá los datos en orden descendente cuando se repita. Para cambiar el orden, simplemente cambie> a <en la instrucción if.
No es el tipo más rápido, pero hace el trabajo sin ninguna dependencia adicional.
fuente