Eu sou relativamente novo em Java e frequentemente acho que preciso classificar Map<Key, Value>
os valores.
Como os valores não são exclusivos, eu me pego convertendo o keySet
em um array
e classificando essa matriz através da classificação de matriz com um comparador personalizado que classifica o valor associado à chave.
Existe uma maneira mais fácil?
List<Map.Entry<...>> list =new LinkedList(map.entrySet())
eCollections.sort ....
assim.Respostas:
Aqui está uma versão genérica:
fonte
forEachOrdered
vez deforEach
, uma vez que os documentos dosforEach
estados: "O comportamento desta operação é explicitamente não determinístico."?Nota importante:
Esse código pode ser quebrado de várias maneiras. Se você pretende usar o código fornecido, leia também os comentários para estar ciente das implicações. Por exemplo, os valores não podem mais ser recuperados por sua chave. (
get
sempre retornanull
.)Parece muito mais fácil do que tudo o que precede. Use um TreeMap da seguinte maneira:
Resultado:
fonte
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);
O Java 8 oferece uma nova resposta: converta as entradas em um fluxo e use os combinadores comparadores do Map.Entry:
Isso permitirá consumir as entradas classificadas em ordem crescente de valor. Se você deseja um valor decrescente, basta reverter o comparador:
Se os valores não forem comparáveis, você pode passar um comparador explícito:
Você pode então usar outras operações de fluxo para consumir os dados. Por exemplo, se você deseja o top 10 em um novo mapa:
Ou imprima para
System.out
:fonte
parallelStream()
neste caso?Três respostas em uma linha ...
Eu usaria o
Google CollectionsGuava para fazer isso - se seus valores foremComparable
, você poderá usarO 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
Eles podem ser aplicados a um TreeMap (à medida que
Ordering
se estendeComparator
) ou a um LinkedHashMap após alguma classificaçãoNB : 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
):= 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 (comohashCode
,equals
ecompareTo
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.
Agora isso provavelmente funcionará, mas:
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ápidoO 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()
ecompare()
->get()
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
fonte
Ordering
é simplesmente um ricoComparator
. Eu tentei comentar cada exemplo (o itálico embaixo de cada um). "natural" indica que os objetos sãoComparable
; é como o ComparableComparator do apache common.onResultOf
aplica uma função ao item que está sendo comparado. Então se você tivesse uma função que adicionou 1 para um inteiro, em seguida,natural().onResultOf(add1Function).compare(1,2)
iria acabar fazendo2.compareTo(3)
ImmutableSetMultiMap
ouImmutableListMultiMap
conter a coleção de variáveis duplicadas.Em http://www.programmersheaven.com/download/49349/download.aspx
fonte
Com o Java 8, você pode usar a API de fluxos para fazê-lo de uma maneira significativamente menos detalhada:
fonte
Collections.reverseOrder(comparing(Entry::getValue))
Entry.comparingByValue(Comparator.reverseOrder())
A classificação das chaves requer que o Comparador pesquise cada valor para cada comparação. Uma solução mais escalável usaria o entrySet diretamente, desde então o valor estaria imediatamente disponível para cada comparação (embora eu não tenha feito backup disso por números).
Aqui está uma versão genérica de uma coisa dessas:
Existem maneiras de diminuir a rotação da memória para a solução acima. O primeiro ArrayList criado pode, por exemplo, ser reutilizado como um valor de retorno; isso exigiria a supressão de alguns avisos genéricos, mas pode valer a pena pelo código de biblioteca reutilizável. Além disso, o comparador não precisa ser realocado a cada chamada.
Aqui está uma versão mais eficiente, embora menos atraente:
Por fim, se você precisar acessar continuamente as informações classificadas (em vez de apenas classificá-las de vez em quando), poderá usar um multi-mapa adicional. Deixe-me saber se você precisar de mais detalhes ...
fonte
A biblioteca de coleções comuns contém uma solução chamada TreeBidiMap . Ou você pode dar uma olhada na API de coleções do Google. Possui TreeMultimap que você pode usar.
E se você não quiser usar essa estrutura ... eles vêm com código fonte.
fonte
Analisei as respostas fornecidas, mas muitas são mais complicadas do que o necessário ou removem elementos do mapa quando várias chaves têm o mesmo valor.
Aqui está uma solução que eu acho que se encaixa melhor:
Observe que o mapa é classificado do valor mais alto para o mais baixo.
fonte
Para fazer isso com os novos recursos do Java 8:
As entradas são ordenadas por seus valores usando o comparador fornecido. Como alternativa, se seus valores forem mutuamente comparáveis, nenhum comparador explícito será necessário:
A lista retornada é uma captura instantânea do mapa fornecido no momento em que esse método é chamado, portanto, nenhum deles refletirá alterações subseqüentes no outro. Para uma visualização iterável ao vivo do mapa:
A iterável retornada cria uma nova captura instantânea do mapa fornecido toda vez que é iterada, impedindo modificações simultâneas, sempre refletindo o estado atual do mapa.
fonte
Crie um comparador personalizado e use-o enquanto cria um novo objeto TreeMap.
Use o código abaixo em sua função principal
Resultado:
fonte
Embora eu concorde que a necessidade constante de classificar um mapa seja provavelmente um cheiro, acho que o código a seguir é a maneira mais fácil de fazer isso sem usar uma estrutura de dados diferente.
}
E aqui está um teste de unidade embaraçosamente incompleto:
}
O resultado é uma lista classificada de objetos Map.Entry, da qual você pode obter as chaves e os valores.
fonte
Use um comparador genérico como:
fonte
A resposta votada mais não funciona quando você tem 2 itens iguais. o TreeMap deixa valores iguais.
o exemplo: mapa não classificado
resultados
Então deixa de fora E !!
Para mim, funcionou bem para ajustar o comparador, se for igual a não retornar 0, mas -1.
no exemplo:
agora retorna:
mapa não classificado:
resultados:
como resposta a Aliens (22 de novembro de 2011): Estou usando esta solução para um mapa de IDs e nomes de números inteiros, mas a idéia é a mesma; portanto, o código acima pode não estar correto (escreverei em um teste e forneça o código correto), este é o código para uma classificação de mapa, com base na solução acima:
e esta é a classe de teste (eu apenas a testei, e isso funciona para o inteiro, mapa de cadeias:
aqui está o código para o comparador de um mapa:
e esta é a caixa de teste para isso:
de corte, você pode tornar isso muito mais genérico, mas eu só precisava disso para 1 caso (o Mapa)
fonte
Em vez de usar
Collections.sort
como alguns, sugiro usarArrays.sort
. Na verdade, o queCollections.sort
faz é algo como isto:Apenas chama
toArray
a lista e depois usaArrays.sort
. Dessa forma, todas as entradas do mapa serão copiadas três vezes: uma vez do mapa para a lista temporária (seja uma LinkedList ou ArrayList), depois para a matriz temporária e, finalmente, para o novo mapa.Minha solução omite esta etapa, pois não cria LinkedList desnecessário. Aqui está o código, compatível com genéricos e com desempenho ideal:
fonte
Esta é uma variação da resposta de Anthony, que não funciona se houver valores duplicados:
Observe que é bastante no ar como lidar com nulos.
Uma vantagem importante dessa abordagem é que ela realmente retorna um mapa, diferente de outras soluções oferecidas aqui.
fonte
Melhor Abordagem
Resultado
fonte
Grande problema. Se você usar a primeira resposta (o Google o leva aqui), altere o comparador para adicionar uma cláusula igual; caso contrário, você não poderá obter valores do mapa_detalhado por chaves:
fonte
Já existem muitas respostas para essa pergunta, mas nenhuma me forneceu o que eu estava procurando, uma implementação de mapa que retorna chaves e entradas classificadas pelo valor associado e mantém essa propriedade à medida que as chaves e os valores são modificados no mapa. Duas outras perguntas pedem isso especificamente.
Elaborei um exemplo amigável genérico que resolve esse caso de uso. Essa implementação não respeita todos os contratos da interface Map, como refletir mudanças e remoções de valor nos conjuntos retornados de keySet () e entrySet () no objeto original. Eu senti que essa solução seria muito grande para incluir em uma resposta de estouro de pilha. Se eu conseguir criar uma implementação mais completa, talvez eu a publique no Github e, em seguida, no link em uma versão atualizada desta resposta.
fonte
Entrada tardia.
Com o advento do Java-8, podemos usar fluxos para manipulação de dados de uma maneira muito fácil / sucinta. Você pode usar fluxos para classificar as entradas do mapa por valor e criar um LinkedHashMap que preserva a iteração da ordem de inserção .
Por exemplo:
Para pedidos inversos, substitua:
com
fonte
Entry.comparingByValue()
(como as assilias respondem acima stackoverflow.com/a/22132422/1480587 ) oucomparing(Entry<Key,Value>::getValue).thenComparing(Entry::getKey)
que você usou? Eu entendo que você também compara chaves se os valores forem idênticos, certo? Notei que a classificação mantém a ordem dos elementos com o mesmo valor - então a classificação por chaves é necessária se as chaves já tiverem sido classificadas antes?Mapa dado
Classifique o mapa com base no valor em ordem crescente
Classifique o mapa com base no valor em ordem decrescente
Resultado:
{software = 50, tecnologia = 70, EUA = 100, empregos = 200, oportunidade = 200}
{empregos = 200, oportunidade = 200, EUA = 100, tecnologia = 70, software = 50}
fonte
Dependendo do contexto, o uso de
java.util.LinkedHashMap<T>
qual lembra a ordem na qual os itens são colocados no mapa. Caso contrário, se você precisar classificar valores com base em sua ordem natural, eu recomendaria manter uma lista separada, que pode ser classificada viaCollections.sort()
.fonte
Como o TreeMap <> não funciona para valores que podem ser iguais, usei o seguinte:
Você pode querer colocar a lista em um LinkedHashMap , mas se você quiser iterar imediatamente, isso é supérfluo ...
fonte
Isso é muito complicado. Os mapas não deveriam fazer esse trabalho, classificando-os por Valor. A maneira mais fácil é criar sua própria classe para que ela atenda às suas necessidades.
No exemplo inferior, você deve adicionar ao TreeMap um comparador no local onde * está. Mas, pela API java, ele fornece apenas chaves para o comparador, não valores. Todos os exemplos aqui declarados são baseados em 2 mapas. Um Hash e uma nova Árvore. O que é estranho.
O exemplo:
Então, mude o mapa para um conjunto da seguinte maneira:
Você criará classe
Results
,e a classe Comparator:
Dessa forma, você pode adicionar facilmente mais dependências.
E como último ponto, adicionarei um iterador simples:
fonte
Com base no código @devinmoore, um mapa classifica métodos usando genéricos e dando suporte à ordem crescente e decrescente.
fonte
Aqui está uma solução OO (ou seja, não usa
static
métodos):Por este meio doado ao domínio público.
fonte
A maneira mais limpa é usar coleções para classificar o mapa em valor:
fonte
Algumas alterações simples para ter um mapa classificado com pares que têm valores duplicados. No método de comparação (classe ValueComparator), quando os valores são iguais, não retornam 0, mas retornam o resultado da comparação das 2 chaves. As chaves são distintas em um mapa, assim você consegue manter valores duplicados (que são classificados por chaves a propósito). Portanto, o exemplo acima pode ser modificado assim:
fonte
Com certeza a solução de Stephen é realmente ótima, mas para quem não pode usar o Goiaba:
Aqui está minha solução para classificar por valor um mapa. Esta solução lida com o caso em que há o dobro do mesmo valor, etc ...
O executivo: http://www.ideone.com/dq3Lu
A saída:
Espero que ajude algumas pessoas
fonte
Se você tiver chaves duplicadas e apenas um pequeno conjunto de dados (<1000) e seu código não for crítico para o desempenho, basta fazer o seguinte:
inputUnsortedMap é a entrada para o código.
A variável classificadaOutputMap conterá os dados em ordem decrescente quando iterados. Para alterar a ordem, basta alterar> para <na instrução if.
Não é o tipo mais rápido, mas executa o trabalho sem nenhuma dependência adicional.
fonte