Agregar múltiples entradas a un HashMap a la vez en una declaración

138

Necesito inicializar un HashMap constante y me gustaría hacerlo en una declaración de línea. Evitando algo como esto:

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

similar a esto en el 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] 

No he encontrado ningún ejemplo que muestre cómo hacer esto después de haber visto tantos.

usuario387184
fuente

Respuestas:

258

Puedes hacerlo:

Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
     put("One", 1);
     put("Two", 2);
     put("Three", 3);
}};
Eng.Fouad
fuente
11
@ user387184 Sí, lo llaman "inicializador de doble paréntesis". Vea este tema: stackoverflow.com/questions/924285/…
Eng.Fouad
2
Acabo de ponerlo en mi código y aparece esta advertencia / mensaje en la línea: "La clase serializable no declara un campo estático final serialVersionUID de tipo largo". ¿Puedo ignorar eso? ¿Qué significa esto? Gracias
user387184
31
No deberías usar este método. Crea una nueva clase para cada vez que la usa, lo que tiene un rendimiento mucho peor que simplemente crear un mapa. Ver stackoverflow.com/questions/924285/…
Timo Türschmann
77
La razón por la que rechacé esto es porque no explica que esto crea una nueva clase por cada vez que la usas. Creo que la gente debería ser consciente de las ventajas y desventajas de hacerlo de esta manera.
idungotnosn
66
@ TimoTürschmann Parece que si alguna vez necesito la inicialización estática de un mapa como este, también sería estático, eliminando la penalización de rendimiento cada vez que lo use : tendría esa penalización una vez. No puedo ver en otro momento que uno quisiera este tipo de inicialización sin que la variable sea estática (por ejemplo, ¿alguien usaría esto en un bucle?). Sin embargo, puedo estar equivocado, los programadores son inventivos.
Chris Cirefice
65

Puede usar ImmutableMap de Google Guava. Esto funciona siempre que no te importe modificar el Mapa más tarde (no puedes llamar a .put () en el mapa después de construirlo 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();

Ver también: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html

Una pregunta algo relacionada: ¿ Solución alternativa de ImmutableMap.of () para HashMap en Maps?

JimmyT
fuente
La guayaba es enorme, yo no lo uso para mi aplicación Android a menos que sea absolutamente necesario
ericn
44
Cuidado que ImmutableMapno acepta nullclaves o valores.
Vadzim
@ericn ProGuard le permite excluir cualquier parte de una biblioteca que no esté utilizando.
dimo414
55

Desde Java 9, es posible usar Map.of(...), así:

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

Este mapa es inmutable. Si desea que el mapa sea mutable, debe agregar:

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

Si no puede usar Java 9, tiene que escribir un método auxiliar similar usted mismo o usar una biblioteca de terceros (como Guava ) para agregar esa funcionalidad por usted.

Timo Türschmann
fuente
Después de agregar 10 entradas, arroja un extraño error "no se puede resolver el método".
vikramvi
2
@vikramvi sí Si miras la documentación Map.of solo se realizan hasta 10 entradas ya que es bastante laborioso
jolivier
8

Java no tiene un mapa literal, por lo que no hay una buena manera de hacer exactamente lo que está pidiendo.

Si necesita ese tipo de sintaxis, considere algunos Groovy, que es compatible con Java y le permite hacer:

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

Los mapas también han agregado métodos de fábrica en Java 9. Para hasta 10 entradas, los mapas han sobrecargado a los constructores que toman pares de claves y valores. Por ejemplo, podríamos construir un mapa de varias ciudades y sus poblaciones (según Google en octubre de 2016) de la siguiente manera:

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

El caso de var-args para Map es un poco más difícil, necesita tener claves y valores, pero en Java, los métodos no pueden tener dos parámetros var-args. Entonces, el caso general se maneja tomando un método var-args de Map.Entry<K, V>objetos y agregando un entry()método estático que los construya. Por ejemplo:

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

Métodos de Factory Factory en Java 9

Sadiq Ali
fuente
Excelente si pudieras usar Java 9+. Además, este método de fábrica devuelve un mapa inmutable.
Sourabh
6

Aquí hay una clase simple que logrará lo que quieres

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]);
    }
}

Y luego usarlo

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

Esto produce {a:1, b:2}

Dakusan
fuente
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 la declaración de x(que es necesaria para evitar un diagnóstico de "declaración inalcanzable"), técnicamente es solo una declaración.

Hot Licks
fuente
14
Esto es asquerosamente hacky.
Micah Stairs
1
@MicahStairs - Pero es solo una declaración.
Hot Licks
2
Es cierto, pero este es el tipo de código que nunca espero encontrar en la producción.
Micah Stairs
@MicahStairs - He visto cosas peores.
Hot Licks
1
Omg, he buscado esto hoy, ¿cómo funciona este código? Lo he agregado al código para probarlo, pero no puedo entender cómo funciona internamente ... :)
GOXR3PLUS
1

Puede agregar esta función de utilidad a una clase de utilidad:

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: en Java 9puedes usar Map.of

R. Oosterholt
fuente
-1

Otro enfoque puede ser escribir una función especial para extraer todos los valores de los elementos de una cadena por expresión 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
fuente