adicionando várias entradas a um HashMap de uma só vez em uma instrução

138

Eu preciso inicializar um HashMap constante e gostaria de fazê-lo em uma instrução de linha. Evitando sth assim:

  hashMap.put("One", new Integer(1)); // adding value into HashMap
  hashMap.put("Two", new Integer(2));      
  hashMap.put("Three", new Integer(3));

semelhante a isso no objetivo C:

[NSDictionary dictionaryWithObjectsAndKeys:
@"w",[NSNumber numberWithInt:1],
@"K",[NSNumber numberWithInt:2],
@"e",[NSNumber numberWithInt:4],
@"z",[NSNumber numberWithInt:5],
@"l",[NSNumber numberWithInt:6],
nil] 

Não encontrei nenhum exemplo que mostre como fazer isso, tendo analisado tantos.

user387184
fonte

Respostas:

258

Você consegue fazer isso:

Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
     put("One", 1);
     put("Two", 2);
     put("Three", 3);
}};
Eng.Fouad
fonte
11
@ user387184 Sim, eles chamam de "inicializador de cinta dupla". Veja este tópico: stackoverflow.com/questions/924285/…
Eng.Fouad
2
Acabei de colocá-lo no meu código e recebo este aviso / mensagem na linha: "A classe serializável não declara um campo final estático serialVersionUID do tipo long". Posso simplesmente ignorar isso? O que isto significa? Graças
user387184
31
Você não deve usar esse método. Ele cria uma nova classe para cada vez que você a usa, com desempenho muito pior do que simplesmente criar um mapa. Veja stackoverflow.com/questions/924285/…
Timo Türschmann
7
A razão pela qual eu diminuí o voto foi porque não explicou que isso cria uma nova classe para cada vez que você a usa. Eu acho que as pessoas devem estar cientes das vantagens de fazer dessa maneira.
Idgotnosn
6
@ TimoTürschmann Parece que se eu precisasse da inicialização estática de um mapa como este, também seria estático, eliminando toda vez que você o usasse penalidade de desempenho - você teria essa penalidade uma vez. Não vejo outra hora em que alguém queira esse tipo de inicialização sem que a variável seja estática (por exemplo, alguém usaria isso em loop?). No entanto, posso estar errado, os programadores são inventivos.
Chris Cirefice
65

Você pode usar o ImmutableMap do Google Guava. Isso funciona desde que você não se preocupe em modificar o mapa posteriormente (não é possível chamar .put () no mapa depois de construí-lo usando este método):

import com.google.common.collect.ImmutableMap;

// For up to five entries, use .of()
Map<String, Integer> littleMap = ImmutableMap.of(
    "One", Integer.valueOf(1),
    "Two", Integer.valueOf(2),
    "Three", Integer.valueOf(3)
);

// For more than five entries, use .builder()
Map<String, Integer> bigMap = ImmutableMap.<String, Integer>builder()
    .put("One", Integer.valueOf(1))
    .put("Two", Integer.valueOf(2))
    .put("Three", Integer.valueOf(3))
    .put("Four", Integer.valueOf(4))
    .put("Five", Integer.valueOf(5))
    .put("Six", Integer.valueOf(6))
    .build();

Veja também: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html

Uma pergunta um tanto relacionada: solução alternativa ImmutableMap.of () para o HashMap no Maps?

JimmyT
fonte
Goiaba é enorme, eu não iria usá-lo para meu aplicativo Android menos que seja absolutamente necessário
ericn
4
Cuidado que ImmutableMapnão aceita nullchaves ou valores.
Vadzim
O @ericn ProGuard permite excluir qualquer parte de uma biblioteca que você não esteja usando.
precisa saber é o seguinte
55

Desde o Java 9, é possível usar Map.of(...), assim:

Map<String, Integer> immutableMap = Map.of("One", 1, 
                                           "Two", 2, 
                                           "Three", 3);

Este mapa é imutável. Se você deseja que o mapa seja mutável, é necessário adicionar:

Map<String, Integer> hashMap = new HashMap<>(immutableMap);

Se você não pode usar o Java 9, você está gravando um método auxiliar semelhante ou usando uma biblioteca de terceiros (como o Guava ) para adicionar essa funcionalidade a você.

Timo Türschmann
fonte
Depois de adicionar 10 entradas, ele lança um erro estranho "não é possível resolver o método", esse erro ocorre com este método?
vikramvi
2
@vikramvi yes Se você olhar para a documentação, Map.of apenas dez entradas são feitas, uma vez que é bastante trabalhoso
jolivier 18/06
8

Java não tem mapa literal, portanto, não há uma maneira agradável de fazer exatamente o que você está pedindo.

Se você precisar desse tipo de sintaxe, considere algum Groovy, que é compatível com Java e permite:

def map = [name:"Gromit", likes:"cheese", id:1234]
dfraser
fonte
8

Os mapas também tiveram métodos de fábrica adicionados no Java 9. Para até 10 entradas, os mapas sobrecarregaram os construtores que recebem pares de chaves e valores. Por exemplo, podemos construir um mapa de várias cidades e suas populações (de acordo com o Google em outubro de 2016) da seguinte forma:

Map<String, Integer> cities = Map.of("Brussels", 1_139000, "Cardiff", 341_000);

O caso var-args do Map é um pouco mais difícil, você precisa ter chaves e valores, mas em Java, os métodos não podem ter dois parâmetros var-args. Portanto, o caso geral é tratado usando um método de Map.Entry<K, V>objetos var-args e adicionando um entry()método estático que os constrói. Por exemplo:

Map<String, Integer> cities = Map.ofEntries(
    entry("Brussels", 1139000), 
    entry("Cardiff", 341000)
);

Métodos de Fábrica de Coleta em Java 9

Sadiq Ali
fonte
Excelente se você pudesse usar o Java 9+. Além disso, esse método de fábrica retorna um mapa imutável.
Sourabh #
6

Aqui está uma classe simples que realizará o que você deseja

import java.util.HashMap;

public class QuickHash extends HashMap<String,String> {
    public QuickHash(String...KeyValuePairs) {
        super(KeyValuePairs.length/2);
        for(int i=0;i<KeyValuePairs.length;i+=2)
            put(KeyValuePairs[i], KeyValuePairs[i+1]);
    }
}

E então usá-lo

Map<String, String> Foo=QuickHash(
    "a", "1",
    "b", "2"
);

Isso gera {a:1, b:2}

Dakusan
fonte
4
    boolean x;
    for (x = false, 
        map.put("One", new Integer(1)), 
        map.put("Two", new Integer(2)),      
        map.put("Three", new Integer(3)); x;);

Ignorando a declaração de x(que é necessária para evitar um diagnóstico de "declaração inacessível"), tecnicamente é apenas uma declaração.

Hot Licks
fonte
14
Isso é repugnantemente hacky.
Micah Stairs
1
@MicahStairs - Mas é apenas uma afirmação.
Hot Licks
2
É verdade, mas esse é o tipo de código que eu nunca espero encontrar na produção.
Micah Stairs #
@MicahStairs - já vi coisas piores.
Hot Licks
1
Omg, eu procurei por isso hoje, como esse código funciona? Eu adicionei-o no código para o teste, mas eu não consigo entender como ele funciona internamente ... :)
GOXR3PLUS
1

Você pode adicionar esta função de utilitário a uma classe de utilitário:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>();

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = YourClass.mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = YourClass.mapOf("key1", "value1", "key2", "value2");

Nota: Java 9você pode usar o Map.of

R. Oosterholt
fonte
-1

Outra abordagem pode estar escrevendo uma função especial para extrair todos os valores dos elementos de uma string por expressão regular:

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
    public static void main (String[] args){
        HashMap<String,Integer> hashMapStringInteger = createHashMapStringIntegerInOneStat("'one' => '1', 'two' => '2' , 'three'=>'3'  ");

        System.out.println(hashMapStringInteger); // {one=1, two=2, three=3}
    }

    private static HashMap<String, Integer> createHashMapStringIntegerInOneStat(String str) {
        HashMap<String, Integer> returnVar = new HashMap<String, Integer>();

        String currentStr = str;
        Pattern pattern1 = Pattern.compile("^\\s*'([^']*)'\\s*=\\s*>\\s*'([^']*)'\\s*,?\\s*(.*)$");

        // Parse all elements in the given string.
        boolean thereIsMore = true;
        while (thereIsMore){
            Matcher matcher = pattern1.matcher(currentStr);
            if (matcher.find()) {
                returnVar.put(matcher.group(1),Integer.valueOf(matcher.group(2)));
                currentStr = matcher.group(3);
            }
            else{
                thereIsMore = false;
            }
        }

        // Validate that all elements in the given string were parsed properly
        if (currentStr.length() > 0){
            System.out.println("WARNING: Problematic string format. given String: " + str);
        }

        return returnVar;
    }
}
Uri Ziv
fonte