Mapa ordenado Java

322

Em Java, existe um objeto que funciona como um mapa para armazenar e acessar pares de chave / valor, mas pode retornar uma lista ordenada de chaves e uma lista ordenada de valores, de modo que as listas de chaves e valores estejam na mesma ordem?

Portanto, como explicação por código, estou procurando algo que se comporte como meu OrderedMap fictício:

OrderedMap<Integer, String> om = new OrderedMap<>();
om.put(0, "Zero");
om.put(7, "Seven");

String o = om.get(7); // o is "Seven"
List<Integer> keys = om.getKeys();
List<String> values = om.getValues();

for(int i = 0; i < keys.size(); i++)
{
    Integer key = keys.get(i);
    String value = values.get(i);
    Assert(om.get(key) == value);
}
O que é isso
fonte
4
Se tudo o que você deseja fazer é percorrer os dois ao mesmo tempo, Map.entrySet () permitirá fazer isso em qualquer mapa. O LinkedHashMap tem uma ordem bem definida, mas para qualquer Mapa, o conjunto de entradas reflete os pares de chave / valor.
Pete Kirkham
5
Esse código não é um bom exemplo, pois qualquer implementação de mapa se comportará como seu código de exemplo. ordenada, ordenada ou não.
31540 Peter Lawrey
2
Na implementação do Sun JDK, os conjuntos retornados pelos conjuntos getKeys e getValues ​​() são apoiados pelo entrySet () no mapa, portanto, terão a mesma ordem de iteração, que é testada por sua amostra.
Pete Kirkham
4
Bem, isso é interessante, eu nunca percebi isso. Ainda assim, me chame de louco, mas prefiro não fazer suposições sobre a implementação que não sejam explicitamente verificadas pela interface. Fui queimado muitas vezes fazendo isso no passado.
Whatsit
2
Este deve ser nomeado Mapa Ordenado Java, pois Mapa Ordenado é algo diferente - consulte LinkedHashMap.
Ondra Žižka

Respostas:

398

O SortedMap de interface (com a implementação TreeMap ) deve ser seu amigo.

A interface possui os métodos:

  • keySet() que retorna um conjunto de chaves em ordem crescente
  • values() que retorna uma coleção de todos os valores na ordem crescente das chaves correspondentes

Portanto, essa interface atende exatamente aos seus requisitos. No entanto, as chaves devem ter uma ordem significativa. Caso contrário, você pode usar o LinkedHashMap onde o pedido é determinado pelo pedido de inserção.

dmeister
fonte
2
exemplo: SortedMap <String, Object> map = new TreeMap <> ();
Ben
7
Para usar o TreeMap, era necessário que a classe de chave tivesse que implementar a interface Comparável. Caso contrário, algum tipo de RuntimeException será lançado. O TreeMap também é um mapa classificado, mas acho que o autor deseja usar apenas o mapa ordenado (não classificado). LinkedHashMap, é uma boa opção obter apenas o mapa ordenado (como você disse, "determinado pelo pedido de inserção").
K. Gol
1
esta resposta pode ser melhorada, mostrando como iterar sobre keySet ()
4
Do java 8 doc . LinkedHashMapcuja ordem de iteração é a ordem em que suas entradas foram acessadas pela última vez
TRiNE
1
@TRiNE Não sigo seu comentário, mas posso ter perdido algum contexto. Por padrão LinkedHashMap, a ordem de iteração é a ordem de inserção, mas você pode usar um construtor diferente para especificar a ordem de acesso. docs.oracle.com/javase/8/docs/api/java/util/…
rob
212

Existe um objeto que funciona como um mapa para armazenar e acessar pares de chave / valor, mas pode retornar uma lista ordenada de chaves e uma lista ordenada de valores, de modo que as listas de chaves e valores estejam na mesma ordem?

Você está procurando java.util.LinkedHashMap . Você obterá uma lista dos pares Map.Entry <K, V> , que sempre são iterados na mesma ordem. Essa ordem é igual à ordem em que você coloca os itens. Como alternativa, use o java.util.SortedMap , onde as chaves devem ter uma ordem natural ou especificada por a Comparator.

John Feminella
fonte
14
E, para salvar o leitor, verifique duas vezes isso, porque é difícil verificar por meio de testes, o keySet()método efetivamente retorna um LinkedHashSet que reflete a ordem das suas put()chamadas. Observe que chamadas repetidas put()para a mesma tecla não mudarão a ordem, a menos que você remove()a tenha pressionado previamente.
Glenn Lawrence
Do java 8 doc . LinkedHashMapcuja ordem de iteração é a ordem em que suas entradas foram acessadas pela última vez
TRiNE
24

LinkedHashMap mantém a ordem das chaves.

java.util.LinkedHashMap parece funcionar como um HashMap normal, caso contrário.

VoNWooDSoN
fonte
1
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem - você sempre pode comentar em suas próprias postagens e, quando tiver reputação suficiente , poderá comentar em qualquer post .
ianaya89
2
@ ianaya89 Acho que essa é uma resposta real, mas é muito parecida com a resposta de John Feminella !
T30
1
Se você deseja obter um mapa ordenado, onde as entradas são armazenadas nessa ordem conforme você as coloca no mapa, um LinkedHashMap é a resposta certa. Se você deseja classificar as entradas no formulário de forma independente do mapa, pela ordem em que as coloca, um SortedMap é a resposta certa.
Ralph
@TRiNE O documento java 8 que você vinculou diz que existem dois modos de pedido possíveis: pedido de inserção e pedido de acesso. Você pensa sobre o último, invocado pelo uso de um construtor especial público LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder). O construtor padrão cria uma instância do LinkedHashMap ordenada por inserção.
Artur Łysik
1
@ ArturŁysik sim, é. Foi um erro cometido por mim dias atrás. Eu vou corrigir isso. Estou excluindo o comentário. como eu não posso mais editá-lo
TRiNE 14/02
7

Eu acho que a coleção mais próxima que você obterá do framework é o SortedMap

bruno conde
fonte
3
Eu votaria negativamente nesta resposta se pensasse que valia a pena perder os pontos por ela. Como a resposta acima indica, sua resposta não possui as informações adequadas sobre o LinkedHashMap, e uma pequena explicação do SortedMap também seria legal.
CorayThan
@CorayThan, nesse caso você upvote as melhores respostas, outros não downvote que podem ser correto, mas não o melhor ...
Bruno conde
1
Isso é o que eu fiz. Apenas dizendo que posso entender por que alguém votaria negativamente.
CorayThan
5

Você pode aproveitar a interface NavigableMap que pode ser acessada e atravessada em ordem crescente ou decrescente de chave. Essa interface pretende substituir a interface SortedMap. O mapa navegável geralmente é classificado de acordo com a ordem natural de suas chaves ou por um comparador fornecido no momento da criação do mapa.

Existem três implementações mais úteis: TreeMap , ImmutableSortedMap e ConcurrentSkipListMap .

Exemplo do TreeMap:

TreeMap<String, Integer> users = new TreeMap<String, Integer>();
users.put("Bob", 1);
users.put("Alice", 2);
users.put("John", 3);

for (String key: users.keySet()) {
  System.out.println(key + " (ID = "+ users.get(key) + ")");
}

Resultado:

Alice (ID = 2)
Bob (ID = 1)
John (ID = 3)
Vitalii Fedorenko
fonte
2

tl; dr

Para manter Map< Integer , String >uma ordem classificada por chave, use uma das duas classes que implementam as interfaces SortedMap/ NavigableMap:

  • TreeMap
  • ConcurrentSkipListMap

Se estiver manipulando o mapa em um único encadeamento, use o primeiro TreeMap,. Se estiver manipulando threads, use o segundo ConcurrentSkipListMap,.

Para detalhes, consulte a tabela abaixo e a discussão a seguir.

Detalhes

Aqui está uma tabela gráfica que eu criei mostrando os recursos das dez Mapimplementações incluídas no Java 11.

A NavigableMapinterface é o que SortedMapdeveria ter sido em primeiro lugar. A SortedMaplógica deve ser removida, mas não pode ser, pois algumas implementações de mapas de terceiros podem estar usando a interface.

Como você pode ver nesta tabela, apenas duas classes implementam as interfaces SortedMap/ NavigableMap:

Ambos mantêm as chaves na ordem classificada, por ordem natural (usando o compareTométodo de Comparable( https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ Comparable.html )) ou por uma Comparatorimplementação aprovada. A diferença entre essas duas classes é que a segunda ,,ConcurrentSkipListMap é segura para threads , altamente simultânea .

Consulte também a coluna Ordem de iteração na tabela abaixo.

  • A LinkedHashMapclasse retorna suas entradas pela ordem em que foram originalmente inseridas .
  • EnumMapretorna entradas na ordem em que a classe enum da chave é definida . Por exemplo, um mapa de qual funcionário está cobrindo qual dia da semana ( Map< DayOfWeek , Person >) usa a DayOfWeekclasse enum incorporada ao Java. Essa enumeração é definida com segunda e domingo, primeiro. Portanto, as entradas em um iterador aparecerão nessa ordem.

As outras seis implementações não prometem a ordem em que relatam suas entradas.

Tabela de implementações de mapas no Java 11, comparando seus recursos

Basil Bourque
fonte
0

Eu usei o mapa Simple Hash, lista vinculada e coleções para classificar um mapa por valores.

import java.util.*;
import java.util.Map.*;
public class Solution {

    public static void main(String[] args) {
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
        // sort the linked list using Collections.sort()
        Collections.sort(list, new Comparator<Entry<String, Integer>>(){
        @Override
         public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) {
        return m1.getValue().compareTo(m2.getValue());
        }
      });
      for(Entry<String, Integer> value: list) {
         System.out.println(value);
     }
   }
}

A saída é:

C=0
C++=1
Java=2
Python=3
JavaScript=4
Golang=5
Steffi Keran Rani J
fonte