Como ordenar um Map pelo valor em ves da chave

26 respostas
souarte

olá,
tenho um HashMap<Character, Integer>
queria uma maneira de saber qual chave tem o menor valor, para depois excluí-la,
usei o TreeMap, mas ele ordena pela ordem alfabética.
tem como ele ordenar pelos valores Integer? ou tenho de implementar aquele tal de Coparator?

outra dúvida, achei uma classe PriorityQueue, que é uma fila que ordena pelo menor. consegui usá-la com array, mas não consegui com o meu HashMap, é porque hashMap não é uma Collection? teria como eu usar o hashMap nessa classe?

obrigado!

26 Respostas

LPJava

se nao to enganado Map trabalha baseado em chaves e nao valores… é tanto que vc pode ter valores iguais para chaves diferentes…acho q vc nao pode ordenar pelo valor…

Preco

Tem um jeito sim… É meio gambiarrento, mas fica estiloso…

Primeiro, tu vai ter que criar uma nova classe, herdando de HashMap:

class SortedHashMap<E, T> extends HashMap<E, T> {
    public Iterator<Object> iterator() {
        Collection<T> collection = this.values();
        Object[] array = collection.toArray();
        Arrays.sort(array);
        return Arrays.asList(array).iterator();
    }
}

Depois, é só usar essa classe pra fazer seu map:

SortedHashMap<Character, Integer> map = new SortedHashMap<Character, Integer>();
map.put(new Character('a'), new Integer(5));
map.put(new Character('b'), new Integer(1));
map.put(new Character('c'), new Integer(3));
map.put(new Character('d'), new Integer(4));
map.put(new Character('e'), new Integer(2));

Daí para buscar os valores, é só usar esse iterator() que a classe SortedHashMap sobrescreveu…

Iterator iter = map.iterator();
while (iter.hasNext()) {
    System.out.println(iter.next()); // Ou qualquer outra coisa
}

Peguei esse código desse site aqui: http://www.codeguru.com/forum/archive/index.php/t-284510.html
E dei uma modificada pra encaixar naquilo que tu precisava…

Té mais o/
(Testa e depois comenta se funcionou ou não…)

LPJava

nao pensei na gambiarra… :smiley:

souarte
(Testa e depois comenta se funcionou ou não...)

infelizmente não era o que eu queria, mas obrigado pela ajuda, esse código ordenou pelos valores, mas só mostrou os valores. eu queria que mostrasse os pares chave=valor.
mas eu consegui, até que enfim, fazer algo, eu queria excluir a chave que tivesse menor valor, e acho queconsegui, depois de pensar muito.
sou muito iniciante, então queria que descem uma olhada no código e fizessem críticas, sei que tá ruim o código.

imagino que isso seja gambiarra, eu criei um objeto Letra que implementei comparator, que tem variaveis de instância chave e valor.
peguei as chaves do HashMap para criar os objetos Letra e inseri num PriorityQueue (que faz exatamente oque eu queria), depois é só eu excluir.

public class ExcluirMenor{
	HashMap<Character, Integer> map = new HashMap<Character, Integer>();
	StringBuffer string = new StringBuffer();
	PriorityQueue<Letra> fila = new PriorityQueue<Letra>();

	public void contCaracteres(String fileName) {
		try{
			BufferedReader file = new BufferedReader(new FileReader(fileName));
			String linha = null;
			while((linha = file.readLine()) != null){
				string.append(linha);
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		//imprime um teste.
		System.out.println(string);

		for(int i=0; i<string.length(); i++){
			if((map.get(string.charAt(i)) == null)){
				map.put(string.charAt(i), 1);
			}else{
				map.put(string.charAt(i), map.get(string.charAt(i)) + 1);
			}
		}
		//imprime um teste
		System.out.println(map.toString());

		for(Character character: map.keySet()){
			fila.offer(new Letra(character, map.get(character)));
		}
		//imprime um teste
		while(fila.size() > 0){
			System.out.println(fila.peek().toString()+"   eh esse óhh!!!");
			fila.poll();
		}
	}
}
//classe Letra
public class Letra implements Comparable{
	private Character chave;
	private Integer valor;
	
	public Character getChave() {
		return chave;
	}
	public void setChave(Character chave) {
		this.chave = chave;
	}
	public int getValor() {
		return valor;
	}
	public void setValor(Integer valor) {
		this.valor = valor;
	}
	
	public Letra(Character chave, Integer valor) {
		super();
		this.chave = chave;
		this.valor = valor;
	}
	public String toString() {
		return (chave+"="+valor);
	}
	public int compareTo(Object o) {
		Letra letra = (Letra)o;
		return this.valor.compareTo(letra.valor);
	}
}

Obrigado!!

Preco

Como postula a segunda lei de POG :

A gambiarra primária consistia em usar o valor do mapa para classificar os elementos (quando o correto seria utilizar a chave pra isso). A criação de um objeto próprio pra isso foi uma boa saída, que eu não tinha pensado. Acho que é até mais correta, pro utilização que tu pretende.

Só uma pergunta: Agora que tu já criou o objeto próprio, não poderias abolir o HashMap do teu programa? Poderias usar, quem sabe, um Collection mais apropriado, como um conjunto ou uma lista…

Abraços

souarte

cara, na verdade eu nem sei, mas se tu acha deve ser porque dá certo. é que não sei muita coisa, na verdade nem sei como meu código deu certo.

Valeus!

peron

Acho que está um pouco tarde, mas deixo aqui pra quem quiser, um código que fiz, que ordena Maps por chaves ou valores, com suporte a Generics do Java 5.

MapUtils.

e para usar entao:

SortedHashMap<Character, Integer> map = new SortedHashMap<Character, Integer>();
map.put(new Character('a'), new Integer(5));
map.put(new Character('b'), new Integer(1));
map.put(new Character('c'), new Integer(3));
map.put(new Character('d'), new Integer(4));
map.put(new Character('e'), new Integer(2));

Map ordenado = MapUtils.sortMapByValue(map);

//prontinho, só usar o ordenado agora :)

PS: Postei o link porque o código é extenso.

Saudações

E

Para não criar outro tópico, estou ressuscitando esse. Utilizei a classe MapUtils acima, porém não consegui ordenar meu map pelo valor.

tenho, atraves da API do jung, vários pares, do tipo Pair<String>, onde cada objeto contém dois tipos de objetos, no caso, uma string. Devo contar a frequencia de cada par e colocar o respectivo par com sua frequencia num map. A contagem está ok, mas sempre que faço map.put(par, 2) por exemplo, o número de registros dentro map do tipo null aumenta. O que estou fazendo/deixando de fazer para que isso aconteça?

Como não consegui ordenar pelo valor, creio que pode ter alguma relação com a quantidade de nulls dentro do map…

victorwss

[size=18]Porque que tem tanta gente que gosta de reinventar a roda quadrada?[/size]

Você não precisa criar uma classe ou fazer uma gabiarra para isso. É só usar o que o Java 6 já tem prontinho, bem testado e abençoado pelo JCP:

http://java.sun.com/javase/6/docs/api/java/util/SortedMap.html
http://java.sun.com/javase/6/docs/api/java/util/NavigableMap.html

ramilani12

victorwss:
[size=18]Porque que tem tanta gente que gosta de reinventar a roda quadrada?[/size]

Você não precisa criar uma classe ou fazer uma gabiarra para isso. É só usar o que o Java 6 já tem prontinho, bem testado e abençoado pelo JCP:

http://java.sun.com/javase/6/docs/api/java/util/SortedMap.html
http://java.sun.com/javase/6/docs/api/java/util/NavigableMap.html

E seu quiser ordenar no java 1.4? :stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue:

Voltando ao assunto vc nao poderia implementar um Comparator?

E

já implementei para utilizar com a classe MapUtils acima, porém não funcionou… o Comparator que implementei compara o valor, não a chave.

victorwss

EDIT:
http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html#TreeMap(java.util.Comparator)

E

victorwss:
[size=18]Porque que tem tanta gente que gosta de reinventar a roda quadrada?[/size]

Você não precisa criar uma classe ou fazer uma gabiarra para isso. É só usar o que o Java 6 já tem prontinho, bem testado e abençoado pelo JCP:

http://java.sun.com/javase/6/docs/api/java/util/SortedMap.html
http://java.sun.com/javase/6/docs/api/java/util/NavigableMap.html


quero ordenar os valores, não as chaves.

sergiotaborda

victorwss:
[size=18]Porque que tem tanta gente que gosta de reinventar a roda quadrada?[/size]

Você não precisa criar uma classe ou fazer uma gabiarra para isso. É só usar o que o Java 6 já tem prontinho, bem testado e abençoado pelo JCP:

http://java.sun.com/javase/6/docs/api/java/util/SortedMap.html
http://java.sun.com/javase/6/docs/api/java/util/NavigableMap.html

Humm… talvez porque não a está inventando. :twisted: Do javadoc do SortedMap

Todas as implementações ordenáveis de mapas ordenas as chaves. O que se está querendo é ordenar os valores.

victorwss
EDIT: Ou seja, pode tentar isso:

Map<K, V> map = ;

Collections.sort(map.values());
E

isso mesmo. quero ordenar um map através dos valores.

E
EDIT: Ou seja, pode tentar isso:

Map<K, V> map = …;

Collections.sort(map.values());</blockquote>

vou testar e postar o resultado.
ramilani12

Exatamente seria o caso de implementar um Comparator.

Um link que pode te ajudar: http://paaloliver.wordpress.com/2006/01/24/sorting-maps-in-java/

victorwss

ramilani12:
Exatamente seria o caso de implementar um Comparator.

Um link que pode te ajudar: http://paaloliver.wordpress.com/2006/01/24/sorting-maps-in-java/

Exatamente isso. :smiley:

sergiotaborda
EDIT: Ou seja, pode tentar isso:

Map<K, V> map = …;

Collections.sort(map.values());</blockquote>

Isso não faz nada. Veja a parte em itálico. A coleção de valores é uma view, o que significa que o mapa não mantém uma coleção de valores. O que ele mantém é uma coleção de key-valor chamada entry (Map.Entry). Repare que a iteração dos valores é na ordem das chaves. ser backed significa que a alteração de Map.Entry significa a alteração da coleção e da iteração ( existe um CocurrentModificationException) e não que alteração em map.values() altera o mapa. Mesmo que o sort ordenasse a view ( que por definição não afeta o mapa) ele não alteraria as chaves, logo não alteraria o mapa.

Ordenar um mapa pelos valores não é possivel, a menos que implemente o seu próprio mapa.
A realidade é que ordenar um mapa pelos valores representa um erro na modelagem e no uso do mapa em primeiro lugar.
Mas caso seja muito necessário ha que implementar o mapa de novo reorganizando as Map.Entry com uma nova lógica.

E

victorwss:
ramilani12:
Exatamente seria o caso de implementar um Comparator.

Um link que pode te ajudar: http://paaloliver.wordpress.com/2006/01/24/sorting-maps-in-java/

Exatamente isso. :smiley:


Implementei o exemplo e funciona realmente. Ele ordena os maps pelo valor. :smiley:
Contudo, nem tudo são flores. Se houver mais de uma chave, com o mesmo valor, ela é sobrescrita pelo map seguinte que contém o mesmo valor, mesmo que tenha outra chave…

E
EDIT: Ou seja, pode tentar isso:

Map<K, V> map = …;

Collections.sort(map.values());</blockquote>

Isso não faz nada. Veja a parte em itálico. A coleção de valores é uma view, o que significa que o mapa não mantém uma coleção de valores. O que ele mantém é uma coleção de key-valor chamada entry (Map.Entry). Repare que a iteração dos valores é na ordem das chaves. ser backed significa que a alteração de Map.Entry significa a alteração da coleção e da iteração ( existe um CocurrentModificationException) e não que alteração em map.values() altera o mapa. Mesmo que o sort ordenasse a view ( que por definição não afeta o mapa) ele não alteraria as chaves, logo não alteraria o mapa.

Ordenar um mapa pelos valores não é possivel, a menos que implemente o seu próprio mapa.
A realidade é que ordenar um mapa pelos valores representa um erro na modelagem e no uso do mapa em primeiro lugar.
Mas caso seja muito necessário ha que implementar o mapa de novo reorganizando as Map.Entry com uma nova lógica.

Quero ordernar o map pelo valor para usá-lo numa implementaçãp de um algoritmo de extração de padrões em grafos.

ramilani12

emanoeltadeu:
victorwss:
ramilani12:
Exatamente seria o caso de implementar um Comparator.

Um link que pode te ajudar: http://paaloliver.wordpress.com/2006/01/24/sorting-maps-in-java/

Exatamente isso. :smiley:


Implementei o exemplo e funciona realmente. Ele ordena os maps pelo valor. :smiley:
Contudo, nem tudo são flores. Se houver mais de uma chave, com o mesmo valor, ela é sobrescrita pelo map seguinte que contém o mesmo valor, mesmo que tenha outra chave…

mas veja nos comentários que há uma solução para isso ele utiliza o compareTo do hashcode

T

Digamos que você tenha uma classe Aluno com as chaves únicas “id” e “nome”. (Estou considerando que não há homônimos, só para simplificar. Se houver homônimos você não vai poder usar diretamente um SortedMap, como você vai ver mais tarde).

class Aluno {
    public Long id;
    public String nome;
    public Aluno (final Long id_, final String nome_) { id = id_; nome = nome_; }
    public String toString() { return "[" + id + "," + nome + "]"; }
}

Se você quiser ordenar os elementos dessa classe por id ou por nome, precisa criar 2 SortedMaps, um por id, outro por nome.

SortedMap<Long,Aluno> porId = new TreeMap<Long,Aluno>(new Comparator<Aluno>() {
    public int compare (Aluno a1, Aluno a2) {
        return a1.id.compareTo (a2.id);
    }
});

SortedMap<String,Aluno> porNome = new TreeMap<String,Aluno>(new Comparator<Aluno>() {
    public int compare (Aluno a1, Aluno a2) {
        return a1.nome.compareTo (a2.nome);
    }
});

Quando for inserir ou remover um elemento, você precisa inserir ou remover nos dois Maps.

ramilani12

Bom passei por esse problema tbm por isso recomendei aquele link:

Uma solução que encontrei foi esta:

Tenho um map e a chave é um auto-incremento do banco de dados ate ai nenhum problema mas ao ordenar surgiram chaves diferentes mas como mesmos valores, caso compareTo retornar 0 comparo com valor do HashCode

public int compare(Object key, Object key1)
{
		// TODO Auto-generated method stub
		Long id1 = (Long) key;
		Long id2 = (Long) key1;
		Atividade a1 = (Atividade) atividades.get(id1);
		Atividade a2 = (Atividade) atividades.get(id2);
		
	       //Aqui utilizo compareTo do objeto java.util.Date	
               int c = a1.getDataAtividade().compareTo(a2.getDataAtividade()); 
		
		if (c == 0)
		{	
			Integer h1 = new Integer(a1.hashCode());
			Integer h2 = new Integer(a2.hashCode());
			return h1.compareTo(h2);
		}
		
	return c;
	}
E

sim, utilizei o seguinte método compare e deu certo:

public int compare(Object key1, Object key2) {
            Integer e1 = (Integer) _data.get(key1);
            Integer e2 = (Integer) _data.get(key2);

            int c = e2.compareTo(e1);

            if (0 != c) return c;
           
            Integer h1 = key1.hashCode(), h2 = key2.hashCode();
            return h2.compareTo(h1);
        }

Comparei o segundo com o primeiro porque quero a ordenação feita de modo decrescente.

Criado 28 de novembro de 2007
Ultima resposta 1 de dez. de 2008
Respostas 26
Participantes 9