Como você inicializaria uma estática Map
em Java?
Método 1: inicializador estático
Método 2: inicializador de instância (subclasse anônima) ou algum outro método?
Quais são os prós e os contras de cada um?
Aqui está um exemplo que ilustra os dois métodos:
import java.util.HashMap;
import java.util.Map;
public class Test {
private static final Map<Integer, String> myMap = new HashMap<>();
static {
myMap.put(1, "one");
myMap.put(2, "two");
}
private static final Map<Integer, String> myMap2 = new HashMap<>(){
{
put(1, "one");
put(2, "two");
}
};
}
Map.of
outra coisaMap.ofEntries
, verifique stackoverflow.com/a/37384773/1216775Respostas:
O inicializador de instância é apenas açúcar sintático, neste caso, certo? Não vejo por que você precisa de uma classe anônima extra apenas para inicializar. E não funcionará se a classe que está sendo criada for final.
Você também pode criar um mapa imutável usando um inicializador estático:
fonte
Eu gosto da maneira Guava de inicializar um mapa estático e imutável:
Como você pode ver, é muito conciso (por causa dos métodos convenientes de fábrica
ImmutableMap
).Se você deseja que o mapa tenha mais de 5 entradas, não é mais possível usá-lo
ImmutableMap.of()
. Em vez disso, tenteImmutableMap.builder()
ao longo destas linhas:Para saber mais sobre os benefícios dos utilitários de coleção imutável do Guava, consulte Coleções imutáveis explicadas no Guia do Usuário do Guava .
(Um subconjunto de) Goiaba costumava ser chamado de Coleções do Google . Se você não estiver usando esta biblioteca no seu projeto Java ainda, eu fortemente recomendo tentar it out! A goiaba rapidamente se tornou uma das bibliotecas de terceiros gratuitas mais populares e úteis para Java, conforme concordam os colegas usuários do SO . (Se você é novo, existem excelentes recursos de aprendizado por trás desse link.)
Atualização (2015) : Quanto ao Java 8 , bem, eu ainda usaria a abordagem Guava porque é muito mais limpa do que qualquer outra coisa. Se você não quer a dependência do Guava, considere um método init simples e antigo . O hack com matriz bidimensional e a API do Stream é muito feio se você me perguntar e fica mais feio se você precisar criar um mapa cujas chaves e valores não sejam do mesmo tipo (como
Map<Integer, String>
na pergunta).Quanto ao futuro do Guava em geral, no que diz respeito ao Java 8, Louis Wasserman disse isso em 2014 e [ atualização ] em 2016 foi anunciado que o Guava 21 exigirá e dará suporte adequado ao Java 8 .
Atualização (2016) : Como Tagir Valeev aponta , o Java 9 finalmente tornará isso mais limpo usando nada além de JDK puro, adicionando métodos de fábrica convenientes para coleções:
fonte
Eu usaria:
fonte
O Java 5 fornece essa sintaxe mais compacta:
fonte
HashMap implements Serializable
. Como você realmente cria uma subclasse do HashMap usando esse "truque", cria implicitamente uma classe Serializable. E para isso você deve fornecer um serialUID.Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.
- IntelliJHashMap.equals
é definidoAbstractMap
e funciona em qualquer subclasse de Map, portanto não é uma preocupação aqui. A coisa do operador de diamante é irritante, mas, como mencionado, agora foi resolvido.Uma vantagem do segundo método é que você pode envolvê-lo
Collections.unmodifiableMap()
para garantir que nada atualize a coleção posteriormente:fonte
Aqui está um inicializador de mapa estático de uma linha do Java 8:
Edit: para inicializar um
Map<Integer, String>
como na pergunta, você precisa de algo como isto:Edit (2): Existe uma versão melhorada para o tipo misto do i_am_zero que usa um fluxo de
new SimpleEntry<>(k, v)
chamadas. Confira essa resposta: https://stackoverflow.com/a/37384773/3950982fonte
String[][]
isso nãoObject[][]
é necessário , é necessário). IMHO, essa abordagem é feia (ainda mais com os elencos) e difícil de lembrar; eu não usaria isso sozinho.Map.of
em Java 9+Veja JEP 269 para detalhes. O JDK 9 atingiu a disponibilidade geral em setembro de 2017.
fonte
Map.ofEntries
Java 9
Podemos usar
Map.ofEntries
, chamandoMap.entry( k , v )
para criar cada entrada.Também podemos usar
Map.of
como sugerido por Tagir em sua resposta aqui, mas não podemos ter mais de 10 entradas usandoMap.of
.Java 8 (solução pura)
Podemos criar um fluxo de entradas do mapa. Já temos duas implementações
Entry
emjava.util.AbstractMap
que são SimpleEntry e SimpleImmutableEntry . Neste exemplo, podemos fazer uso do ex como:fonte
new SimpleEntry<>()
caminho é muito menos legível que estáticoput()
: /Com o Eclipse Collections , todos os seguintes itens funcionarão:
Você também pode inicializar estaticamente mapas primitivos com o Eclipse Collections.
Nota: Eu sou um confirmador das Coleções Eclipse
fonte
Eu nunca criaria uma subclasse anônima nessa situação. Inicializadores estáticos funcionam igualmente bem, se você quiser tornar o mapa inalterável, por exemplo:
fonte
Talvez seja interessante conferir as coleções do Google , por exemplo, os vídeos que eles têm em suas páginas. Eles fornecem várias maneiras de inicializar mapas e conjuntos, além de fornecer coleções imutáveis.
Atualização: Esta biblioteca agora se chama Guava .
fonte
Eu gosto de classe anônima, porque é fácil lidar com isso:
fonte
Se declararmos mais de uma constante, esse código será gravado em bloco estático e é difícil de manter no futuro. Portanto, é melhor usar classe anônima.
E é sugerido o uso de unmodifiableMap para constantes; caso contrário, não pode ser tratado como constante.
fonte
Eu poderia sugerir fortemente o estilo "inicialização entre chaves" sobre o estilo de bloco estático.
Alguém pode comentar que não gosta de classe anônima, sobrecarga, desempenho etc.
Mas eu considero mais a legibilidade e a manutenção do código. Nesse ponto de vista, eu acredito que uma chave dupla é um estilo de código melhor do que um método estático.
Além disso, se você conhece o GC da classe anônima, sempre pode convertê-lo em um HashMap normal usando
new HashMap(Map map)
.Você pode fazer isso até enfrentar outro problema. Se o fizer, você deve usar outro estilo de codificação (por exemplo, não estático, classe de fábrica) para ele.
fonte
Como de costume, o apache-commons possui o método apropriado MapUtils.putAll (Map, Object []) :
Por exemplo, para criar um mapa de cores:
fonte
Arrays.asMap( ... )
no Java comum, acho que essa é a melhor solução. Reinventar a roda geralmente é bobagem. Uma desvantagem muito pequena é que, com os genéricos, será necessária uma conversão desmarcada.SuppressWarnings( unchecked )
no Eclipse com uma linha como:Map<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
String[][]
, recebo o "aviso"! E, claro, isso só funciona se vocêK
eV
a mesma classe. Suponho que você não tenha (compreensivelmente) definido "conversão desmarcada" como "Ignorar" em sua configuração do Eclipse?Aqui está o meu favorito quando não quero (ou não posso) usar o Goiaba
ImmutableMap.of()
ou se preciso de um mutávelMap
:É muito compacto e ignora valores perdidos (ou seja, uma chave final sem valor).
Uso:
fonte
Se você deseja um mapa não modificável, finalmente o java 9 adicionou um método legal de fábrica para fazer
of
aMap
interface. Um método semelhante é adicionado ao Set, List também.Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
fonte
Eu prefiro usar um inicializador estático para evitar a geração de classes anônimas (que não teriam mais nenhum objetivo), portanto, listarei dicas de inicialização com um inicializador estático. Todas as soluções / dicas listadas são de tipo seguro.
Nota: A pergunta não diz nada sobre como tornar o mapa inalterável, então deixarei isso de fora, mas sei que isso pode ser feito facilmente
Collections.unmodifiableMap(map)
.Primeira dica
A primeira dica é que você pode fazer uma referência local ao mapa e atribuir um nome CURTO:
Segunda dica
A segunda dica é que você pode criar um método auxiliar para adicionar entradas; você também pode tornar público esse método auxiliar se desejar:
O método auxiliar aqui não é reutilizável, porque apenas pode adicionar elementos
myMap2
. Para torná-lo reutilizável, poderíamos tornar o mapa em si um parâmetro do método auxiliar, mas o código de inicialização não seria mais curto.Terceira dica
A terceira dica é que você pode criar uma classe auxiliar reutilizável do tipo construtor com a funcionalidade de preenchimento. Esta é realmente uma classe auxiliar simples de 10 linhas que é segura para o tipo:
fonte
A classe anônima que você está criando funciona bem. No entanto, você deve estar ciente de que essa é uma classe interna e, como tal, conterá uma referência à instância de classe circundante. Então você descobrirá que não pode fazer certas coisas com ele (usando o XStream para um). Você receberá alguns erros muito estranhos.
Dito isto, desde que você esteja ciente, essa abordagem é adequada. Eu o uso na maioria das vezes para inicializar todos os tipos de coleções de maneira concisa.
Edição: apontou corretamente nos comentários que esta é uma classe estática. Obviamente, não li isso de perto. No entanto os meus comentários que ainda se aplicam a classes internas anônimas.
fonte
Se você quiser algo conciso e relativamente seguro, basta mudar a verificação do tipo em tempo de compilação para o tempo de execução:
Esta implementação deve detectar quaisquer erros:
fonte
Com o Java 8, passei a usar o seguinte padrão:
Não é a rotunda mais concisa e um pouco, mas
java.util
fonte
toMap
assinatura incluindo um fornecedor de mapas para especificar o tipo de mapa.Se você precisar adicionar apenas um valor ao mapa, poderá usar Collections.singletonMap :
fonte
Você pode usar
StickyMap
eMapEntry
de Cactoos :fonte
Sua segunda abordagem (inicialização do Double Brace) é considerada um anti-padrão , então eu usaria a primeira abordagem.
Outra maneira fácil de inicializar um mapa estático é usando esta função de utilitário:
Nota:
Java 9
você pode usar o Map.offonte
Não gosto da sintaxe do inicializador estático e não estou convencido de subclasses anônimas. Geralmente, concordo com todos os contras do uso de inicializadores estáticos e com todos os contras do uso de subclasses anônimas mencionadas nas respostas anteriores. Por outro lado, os profissionais apresentados nesses posts não são suficientes para mim. Eu prefiro usar o método de inicialização estática:
fonte
Eu não vi a abordagem que eu uso (e passei a gostar) postada em nenhuma resposta, então aqui está:
Não gosto de usar inicializadores estáticos porque são desajeitados e não gosto de classes anônimas porque estão criando uma nova classe para cada instância.
em vez disso, prefiro uma inicialização assim:
infelizmente, esses métodos não fazem parte da biblioteca Java padrão; portanto, você precisará criar (ou usar) uma biblioteca de utilitários que define os seguintes métodos:
(você pode usar 'import static' para evitar a necessidade de prefixar o nome do método)
Achei útil fornecer métodos estáticos semelhantes para as outras coleções (lista, conjunto, SortedSet, SortedMap, etc.)
Não é tão bom quanto a inicialização do objeto json, mas é um passo nessa direção, no que diz respeito à legibilidade.
fonte
Como o Java não suporta literais de mapa, as instâncias de mapa sempre devem ser explicitamente instanciadas e preenchidas.
Felizmente, é possível aproximar o comportamento dos literais de mapas em Java usando métodos de fábrica .
Por exemplo:
Resultado:
É muito mais conveniente do que criar e preencher o mapa, um elemento de cada vez.
fonte
O JEP 269 fornece alguns métodos de fábrica convenientes para a API de coleções. Esses métodos de fábrica não estão na versão Java atual, que é 8, mas estão planejados para a liberação do Java 9.
Pois
Map
existem dois métodos de fábrica:of
eofEntries
. Usandoof
, você pode passar pares alternados de chave / valor. Por exemplo, para criar umMap
like{age: 27, major: cs}
:Atualmente, existem dez versões sobrecarregadas para
of
, portanto, você pode criar um mapa contendo dez pares de chave / valor. Se você não gostar dessa limitação ou de valores / chaves alternados, poderá usarofEntries
:Ambos
of
eofEntries
retornará uma imutávelMap
, então você não pode mudar os seus elementos após a construção. Você pode experimentar esses recursos usando o JDK 9 Early Access .fonte
Bem ... eu gosto de enums;)
fonte
Eu li as respostas e decidi escrever meu próprio construtor de mapas. Sinta-se livre para copiar e colar e aproveitar.
EDIT: Ultimamente, continuo encontrando métodos estáticos públicos com
of
bastante frequência e meio que gosto disso. Eu o adicionei no código e tornei o construtor privado, alternando para o padrão de método de fábrica estático.EDIT2: Ainda mais recentemente, não gosto mais do método estático chamado
of
, pois parece muito ruim ao usar importações estáticas. Eu o renomei paramapOf
torná-lo mais adequado para importações estáticas.fonte