Três respostas em uma linha ...
Eu usaria o Google Collections Guava para fazer isso - se seus valores forem Comparable
, você poderá usar
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map))
O que criará uma função (objeto) para o mapa [que pega qualquer uma das teclas como entrada, retornando o respectivo valor] e depois aplica ordem natural (comparável) a elas [os valores].
Se não forem comparáveis, você precisará fazer algo como
valueComparator = Ordering.from(comparator).onResultOf(Functions.forMap(map))
Eles podem ser aplicados a um TreeMap (à medida que Ordering
se estende Comparator
) ou a um LinkedHashMap após alguma classificação
NB : Se você for usar um TreeMap, lembre-se de que, se uma comparação == 0, o item já está na lista (o que acontecerá se você tiver vários valores que comparam o mesmo). Para aliviar isso, você pode adicionar sua chave ao comparador da seguinte maneira (presumindo que suas chaves e valores sejam Comparable
):
valueComparator = Ordering.natural().onResultOf(Functions.forMap(map)).compound(Ordering.natural())
= Aplique a ordem natural ao valor mapeado pela chave e combine isso com a ordem natural da chave
Note-se que este ainda não vai funcionar se suas chaves comparar a 0, mas isso deve ser suficiente para a maioria dos comparable
itens (como hashCode
, equals
e compareTo
são muitas vezes em sincronia ...)
Consulte Ordering.onResultOf () e Functions.forMap () .
Implementação
Então agora que temos um comparador que faz o que queremos, precisamos obter um resultado disso.
map = ImmutableSortedMap.copyOf(myOriginalMap, valueComparator);
Agora isso provavelmente funcionará, mas:
- precisa ser feito, dado um mapa completo concluído
- Não tente os comparadores acima em um
TreeMap
; não adianta tentar comparar uma chave inserida quando ela não tiver um valor até depois do put, ou seja, ela quebrará muito rápido
O ponto 1 é um pouco complicado para mim; as coleções do google são incrivelmente preguiçosas (o que é bom: você pode executar praticamente todas as operações em um instante; o trabalho real é feito quando você começa a usar o resultado) e isso exige a cópia de um mapa inteiro !
Resposta "completa" / mapa ordenado ao vivo por valores
Não se preocupe; se você era obcecado o suficiente por ter um mapa "ativo" classificado dessa maneira, poderia resolver não um, mas os dois (!) dos problemas acima com algo louco como o seguinte:
Nota: Isso mudou significativamente em junho de 2012 - o código anterior nunca funcionaria: é necessário um HashMap interno para pesquisar os valores sem criar um loop infinito entre TreeMap.get()
- - compare()
e 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"));
}
}
Quando colocamos, garantimos que o mapa de hash tenha o valor do comparador e, em seguida, colocamos no TreeSet para classificação. Mas antes disso, verificamos o mapa de hash para ver se a chave não é realmente uma duplicata. Além disso, o comparador que criamos também incluirá a chave para que valores duplicados não excluam as chaves não duplicadas (devido a == comparação). Esses 2 itens são vitais para garantir a manutenção do contrato do mapa; se você acha que não quer isso, está quase no ponto de inverter completamente o mapa (para Map<V,K>
).
O construtor precisaria ser chamado como
new ValueComparableMap(Ordering.natural());
//or
new ValueComparableMap(Ordering.from(comparator));
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
eCollections.sort ....
assim.