Desejo adicionar valores a um HashMap
, que seria usado por métodos no mesma classe. Tenho duas soluções:
- Adicionando todos os valores com
static
- Quando o primeiro método é chamado, adicione os valores
Solução nº 1:
private static Map<Character, String> codes = new HashMap<>(); static { codes.put("A", ".-"); codes.put("B", "-..."); codes.put("C", "-.-."); codes.put("D", "-.."); codes.put("E", "."); codes.put("F", "..-."); // ... }
Solução nº 2:
boolean methodIsCalled = false; public static char decode(String s) { if(!methodIsCalled) { addValues(); methodIsCalled = true; } // ... } private static void addValues() { codes.put("A", ".-"); codes.put("B", "-..."); codes.put("C", "-.-."); codes.put("D", "-.."); codes.put("E", "."); codes.put("F", "..-."); // ... }
Qual é o mais eficiente? Qual é a prática recomendada?
Comentários
- Por que você está colocando isso em um HashMap em primeiro lugar? Por que não um array de 26 strings?
- Como um array de 26 strings resolve esse problema? Em particular, como você faz o mapeamento? Você tem a conversão implícita de A – > 0, B – > 0, …, em mente?
- @Ali um array final lida com imutabilidade e inicialização. A- > 0, B- > 1 faz isso muito bem, evitando muitos dos outros problemas de outras bibliotecas necessárias, blocos estáticos extras ou construtores. É ' claro e razoável.
- @MichaelT Talvez para este exemplo em particular, dado que o usuário deseja ter mapeamento para o código morse, um array de String de tamanho 26 é bom, no entanto, não é uma solução geral. Minha resposta foi para uma solução geral, não esta em particular. Em muitos casos, o domínio é tão grande que não podemos usar um mapeamento direto para inteiros e precisamos usar um hashmap.
- @MichaelT btw, um array só é imutável em termos de tamanho, mas você pode mudar um de seus elementos. a [0] = " novo val ".
Resposta
Sua solução 1 pode ser problemática, pois o hashmap é estático e será inicializado apenas uma vez e é compartilhado por todas as instâncias de sua classe. Este é o seu comportamento pretendido ou você deseja que cada instância tenha seu próprio mapa? Se você apenas um mapa, sugiro passar o conjunto para o construtor em vez de usar um estático, por exemplo:
public class Data { private final Map<Character, String> codes; public Data(Map<Character, String> codes) { this.codes = codes} }
Sua solução 2 adiciona a sobrecarga da inicialização preguiçosa do conjunto, cada vez que você precisar, e adiciona a verificação feia methodIsCalled
à lógica do seu programa. Acho que inicializar o mapa no construtor é uma opção melhor.
public class Data { private final Map<Character, String> codes; public Data() { this.codes = new HashMap<>(); codes.put("A", ".-"); codes.put("B", "-..."); codes.put("C", "-.-."); codes.put("D", "-.."); codes.put("E", "."); codes.put("F", "..-."); } }
A outra pergunta que você precisa responder é se você alterar os valores deste hashmap posteriormente ou não. Se você não alterá-lo, é melhor procurar hashMaps imutáveis. Uma opção é usar Collections.unmodifiableMap (map).
Você também pode usar as bibliotecas do Google Guava para permitir que você inicialize um mapa em uma linha e obter um mapa imutável:
ImmutableMap.<Character, String>builder() .put("A", ".-") .put("B", "-...") .put("C", "-.-.") .put("D", "-..") .put("E", ".") .put("F", "..-.") .build();
Resposta
Nada pode bater ImmutableMap da Guava com seu consumo de memória otimizado, mas aqui está um par de soluções puras:
/* Name of the class has to be "Main" only if the class is public. */ class Ideone { private static final Map<Character, String> codes1; static { Map<Character, String> temp= new HashMap<Character, String>(); temp.put("A", ".-"); temp.put("B", "-..."); temp.put("C", "-.-."); temp.put("D", "-.."); temp.put("E", "."); temp.put("F", "..-."); // ... codes1 = Collections.unmodifiableMap(temp); } private static final Map<Character, String> codes2 = Collections.unmodifiableMap(new HashMap<Character, String>() { { put("A", ".-"); put("B", "-..."); put("C", "-.-."); put("D", "-.."); put("E", "."); put("F", "..-."); // ... } }); }
Resposta
Se você não insiste na inicialização lenta (e para um mapa pequeno e não crescente de 26 itens você não deveria), então por que não otimizar para legibilidade? Eu sempre usaria algo como
private static Map<Character, String> codes = newMap( "A", ".-", "B", "-...", "C", "-.-.", ... );
(com uma função auxiliar adequadamente definida newMap
).
Comentários
- Você pode escrever newMap com pacotes de parâmetros?
- @ VF1 Você pode, se definir como uma função genérica com varargs .
Resposta
Nesses casos, suponho que a pergunta não seja sobre um método eficiente – mas quando você realmente precisa daquele mapa inicializado e pronto ..
no caso de inicialização estática – no momento em que a classe é carregada
sua abordagem de carregamento lento pode ser necessária se você precisar definir “grande “mapear e dizer que muitas vezes esses valores vêm de uma fonte cara (ou seja, através da rede), mesmo assim você pode não precisar de um sinalizador externo.
Collection.isEmpty () diria se já foi inicializado ou não (desde que, é claro, pelo menos um valor esteja lá para ser inicializado)