Como inicializar diretamente um HashMap (de uma forma literal)?

Há alguma forma de inicializar um Java HashMap como este?

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

Qual seria a sintaxe correcta? Eu não encontrei nada sobre isto. Isto é possível? Estou procurando a maneira mais curta/rápida de colocar alguns "final/estático" valores em um mapa que nunca mudam e são conhecidos com antecedência ao criar o Mapa.

Solução

Para Java Versão 9 ou superior:

Sim, isto é possível agora. Em Java 9 foram adicionados alguns métodos de fábrica que simplificam a criação de mapas :

// this works for up to 10 elements:
Map test1 = Map.of(
    "a", "b",
    "c", "d"
);

// this works for any number of elements:
Map test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);

No exemplo acima tanto o test como o test2 serão o mesmo, apenas com formas diferentes de expressar o Mapa. O método Mapa.de' é definido para até dez elementos no mapa, enquanto o métodoMapa.deEntries' não terá tal limite.

Note que neste caso o mapa resultante será um mapa imutável. Se você quiser que o mapa seja mutável, você pode copiá-lo novamente, por exemplo, utilizando mutableMap = new HashMap(Map.of("a", "b"));

(Ver também JEP 269 e o Javadoc)

Para até a versão 8 de Java:

Não, você terá que adicionar todos os elementos manualmente. Você pode usar um inicializador em uma subclasse anônima para tornar a sintaxe um pouco mais curta:

Map myMap = new HashMap() {{
        put("a", "b");
        put("c", "d");
    }};

Entretanto, a subclasse anônima pode introduzir comportamentos indesejados em alguns casos. Isto inclui, por exemplo:

  • Gera uma classe adicional que aumenta o consumo de memória, o consumo de espaço em disco e o tempo de inicialização
  • No caso de um método não estático: Ele contém uma referência ao objeto sobre o qual o método de criação foi chamado. Isso significa que o objeto da classe externa não pode ser coletado enquanto o objeto do mapa criado ainda estiver referenciado, bloqueando assim a memória adicional.

O uso de uma função para inicialização também permitirá que você gere um mapa em um inicializador, mas evita efeitos colaterais desagradáveis:

Map myMap = createMap();

private static Map createMap() {
    Map myMap = new HashMap();
    myMap.put("a", "b");
    myMap.put("c", "d");
    return myMap;
}
Comentários (13)

Isto é uma maneira.

HashMap h = new HashMap() {{
    put("a","b");
}};

No entanto, você deve ter cuidado e certificar-se de compreender o código acima (ele cria uma nova classe que herda do HashMap). Portanto, você deve ler mais aqui: http://www.c2.com/cgi/wiki?DoubleBraceInitialization ou simplesmente usar Goiaba:

Map left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Comentários (13)
Map test = new HashMap()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};
Comentários (8)