Tres respuestas de 1 línea ...
Me gustaría utilizar Google Colecciones de guayaba para hacer esto - si sus valores son Comparable
entonces usted puede utilizar
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
Lo 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
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Estos pueden aplicarse a un TreeMap (como se Ordering
extiende Comparator
), o un LinkedHashMap después de ordenarlos
NB : 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
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= 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 (como hashCode
, equals
y a compareTo
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.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Ahora esto probablemente funcionará, pero:
- debe hacerse con un mapa completo terminado
- No intentes los comparadores anteriores en a
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ápido
El 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()
y compare()
->get()
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
class ValueComparableMap<K extends Comparable<K>,V> extends TreeMap<K,V> {
//A map for doing lookups on the keys for comparison so we don't get infinite loops
private final Map<K, V> valueMap;
ValueComparableMap(final Ordering<? super V> partialValueOrdering) {
this(partialValueOrdering, new HashMap<K,V>());
}
private ValueComparableMap(Ordering<? super V> partialValueOrdering,
HashMap<K, V> valueMap) {
super(partialValueOrdering //Apply the value ordering
.onResultOf(Functions.forMap(valueMap)) //On the result of getting the value for the key from the map
.compound(Ordering.natural())); //as well as ensuring that the keys don't get clobbered
this.valueMap = valueMap;
}
public V put(K k, V v) {
if (valueMap.containsKey(k)){
//remove the key in the sorted set before adding the key again
remove(k);
}
valueMap.put(k,v); //To get "real" unsorted values for the comparator
return super.put(k, v); //Put it in value order
}
public static void main(String[] args){
TreeMap<String, Integer> map = new ValueComparableMap<String, Integer>(Ordering.natural());
map.put("a", 5);
map.put("b", 1);
map.put("c", 3);
assertEquals("b",map.firstKey());
assertEquals("a",map.lastKey());
map.put("d",0);
assertEquals("d",map.firstKey());
//ensure it's still a map (by overwriting a key, but with a new value)
map.put("d", 2);
assertEquals("b", map.firstKey());
//Ensure multiple values do not clobber keys
map.put("e", 2);
assertEquals(5, map.size());
assertEquals(2, (int) map.get("e"));
assertEquals(2, (int) map.get("d"));
}
}
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
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
yCollections.sort ....
así.