Ordenando um Map pelo Valor

Pessoal, preciso ordenar um Map pelo valor. Qual o segredo???

Se você usar um SortedMap ele é ordenado pela chave, não pelo valor.
Para ordenar um SortedMap pelo valor, faça um mapa “reverso”, trocando os valores pelas chaves. Por exemplo:

SortedMap<String,String> numeros = new TreeMap<String,String>();
numeros.put ("um", "eins");
numeros.put ("dois", "zwei");
numeros.put ("tres", "drei");
System.out.println (numeros); // deve imprimir:
// [(dois, zwei), (tres, drei), (um, eins)]
SortedMap<String,String> reverso = new TreeMap<String,String>();
for (String chave : reverso.keys()) {
    reverso.put (numeros.get(chave), chave);
}
System.out.println (reverso); // deve imprimir, ordenado pelo valor:
// [(tres, drei), (um, eins), (dois, zwei)]

Se ordenação do Map é para apresentação vc pode utilizar o método Collections.sort()
Vejamos:


List ll = new LinkedList(); // Collections.sort() recebe como parametro um list
ll.addAll(meuMap.values());// buscando os valores no Map
Collections.sort(ll); // ordenando

[quote=thingol]Se você usar um SortedMap ele é ordenado pela chave, não pelo valor.
Para ordenar um SortedMap pelo valor, faça um mapa “reverso”, trocando os valores pelas chaves. Por exemplo:

SortedMap<String,String> numeros = new TreeMap<String,String>(); numeros.put ("um", "eins"); numeros.put ("dois", "zwei"); numeros.put ("tres", "drei"); System.out.println (numeros); // deve imprimir: // [(dois, zwei), (tres, drei), (um, eins)] SortedMap<String,String> reverso = new TreeMap<String,String>(); for (String chave : reverso.keys()) { reverso.put (numeros.get(chave), chave); } System.out.println (reverso); // deve imprimir, ordenado pelo valor: // [(tres, drei), (um, eins), (dois, zwei)] [/quote]

Thingol,
Estou usando o java 1.3, daí tentei converter seu código. Ah, fiz uma correção: no laço for, eu faço referência ao map numeros, né?!

		SortedMap numeros = new TreeMap();
		 numeros.put ("um", "eins");
		 numeros.put ("dois", "zwei");
		 numeros.put ("tres", "drei");
		 System.out.println (numeros); // deve imprimir:
		 // [(dois, zwei), (tres, drei), (um, eins)]
		 SortedMap reverso = new TreeMap();
		 for (Iterator iter = numeros.keySet().iterator(); iter.hasNext();) {
			 Object obj = iter.next();
		     reverso.put (numeros.get(obj), obj);
		 }
		 System.out.println (reverso); // deve imprimir, ordenado pelo valor:

Ele só ordena as chaves, mas eu preciso passar esse map para um método processá-lo e com as chaves invertidas daria crash.

Pergunta boba número um - por que é que o tal método precisa de um mapa ordenado pelos valores? Não faz sentido (principalmente se o método receber um Map qualquer, inclusive um HashMap, que não tem ordenação nenhuma).

Outra coisa, quando inverto um mapa, como foi o caso que mostrei, estou supondo que há uma relação biunívoca (ou seja, a cada chave corresponde exatamente um valor, e a cada valor corresponde exatamente uma chave).

No exemplo que mostrei não há o tal problema; mas imagine se o mapa fosse, por exemplo, "estado" -&gt "milhões de habitantes". Por exemplo:

SP -&gt 23
RJ -&gt 20
MG -&gt 20
ES -&gt 3

Ao inverter o mapa teríamos algo que não é válido para um Map, que é ter 2 chaves iguais. Nesse caso um dos pares seria perdido:

23 -&gt SP
20 -&gt MG (o valor RJ -&gt 20 seria perdido)
3 -&gt ES

[quote=thingol]Outra coisa, quando inverto um mapa, como foi o caso que mostrei, estou supondo que há uma relação biunívoca (ou seja, a cada chave corresponde exatamente um valor, e a cada valor corresponde exatamente uma chave).

No exemplo que mostrei não há o tal problema; mas imagine se o mapa fosse, por exemplo, "estado" -&gt "milhões de habitantes". Por exemplo:

SP -&gt 23
RJ -&gt 20
MG -&gt 20
ES -&gt 3

Ao inverter o mapa teríamos algo que não é válido para um Map, que é ter 2 chaves iguais. Nesse caso um dos pares seria perdido:

23 -&gt SP
20 -&gt MG (o valor RJ -&gt 20 seria perdido)
3 -&gt ES

[/quote]
Não tinha pensando nesse lance da relação biunívoca, e como meu map tem valores repetidos, então não funcionará fazer a inversão. Depois q eu gero esse map, preciso ordená-lo para gerar uma combobox para o usuário.

Ah, então é melhor fazer o seguinte:
Gere um List, ou um LinkedHashMap, com base nos pares <chave, valor>. Esse List é que deve ser o tal combo. Por exemplo, digamos que você queira mostrar o combo dos estados ordenados decrescentemente pela população:

SP -&gt 23
RJ -&gt 20
MG -&gt 20
ES -&gt 2

O LinkedHashMap é um HashMap, mas ele preserva a ordem de inserção (o Iterator retornado por entrySet() retorna os pares chave -&gt valor na ordem em que foram inseridos, e o Iterator retornado por keySet, as chaves na ordem em que foram inseridas.
Então se você, de alguma forma (talvez usando uma lista temporária) tem esses dados ordenados do jeito que você quer, você pode ter o tal "mapa ordenado pelos valores" que você queria ter.

Achei uma solução, usando a interface Comparator onde consulto o map original para indicar a ordem dos elementos, mas transfiro os itens do map original para um novo map para acontecer a ordenação

Map sortedMap = new TreeMap(new ValueComparator(mapOriginal));
for (Iterator iter = mapOriginal.keySet().iterator(); iter.hasNext();) {
	String key = (String) iter.next();
	sortedMap.put(key, mapOriginal.get(key));
}

Classe ValueComparator:

	public class ValueComparator implements java.util.Comparator {
		private Map m = null; // the original map 

		public ValueComparator(Map m) {
			this.m = m;
		}

		public int compare(Object o1, Object o2) {
			// handle some exceptions here 
			String v1 = (String) m.get(o1);
			String v2 = (String) m.get(o2);
			// make sure the values implement Comparable
			
			return v1.compareTo(v2);
		}
		// do something similar in equals. 

	} 

Obviamente que o meu chutômetro quanto à população dos Estados está bem ruinzinho. No site do IBGE, a população estimada (em 2005) para cada um dos Estados:

SP -&gt 40.442.795
RJ -&gt 15.383.407
ES -&gt 3.408.365
MG -&gt 19.237.450

Somando RJ e ES dá a população de MG.

[quote=thingol]Ah, então é melhor fazer o seguinte:
Gere um List, ou um LinkedHashMap…[/quote]

O Cezar já resolveu o problema dele, mas só um aviso: ele disse que está usando Java 1.3 e o LinkedHashMap só foi inserido no JDK a partir do 1.4 .

Descobri isso do jeito difícil, ao fazer uma aplicação com o JDK 1.4, compilar com o target 1.3 e aparecer um Error 500: LinkedHashSet . :stuck_out_tongue:

Tanto é que em um sistema em que estava trabalhando o meu chefe tinha criado uma classe com o nome “HashMapOrder” e aproximadamente com a mesma funcionalidade de LinkedHashMap, só que não teve o cuidado de fazê-la implementar a interface Map, e outras mancadas.