Pregunta ¿Cómo puedo inicializar un mapa estático?


¿Cómo inicializaría un mapa estático en Java?

Método uno: inicializador estático
Método dos: inicializador de instancia (subclase anónima) o algún otro método?

¿Cuáles son los pros y los contras de cada uno?

Aquí hay un ejemplo que ilustra dos métodos:

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<Integer, String>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<Integer, String>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

930
2018-02-03 15:41


origen


Respuestas:


El inicializador de instancia es solo azúcar sintáctico en este caso, ¿verdad? No veo por qué necesitas una clase anónima adicional solo para inicializar. Y no funcionará si la clase que se está creando es definitiva.

También puede crear un mapa inmutable utilizando un inicializador estático:

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

945
2017-08-31 13:58



me gusta el Guayaba forma de inicializar un mapa estático e inmutable:

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

Como puede ver, es muy conciso (debido a los convenientes métodos de fábrica en ImmutableMap)

Si desea que el mapa tenga más de 5 entradas, ya no puede usar ImmutableMap.of(). En cambio, prueba ImmutableMap.builder() a lo largo de estas líneas:

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

Para obtener más información sobre los beneficios de las utilidades de recolección inmutables de Guava, consulte Colecciones Inmutables Explicadas en Guava Guía del usuario.

(Un subconjunto de) Se solía llamar guayaba Colecciones de Google. Si todavía no está utilizando esta biblioteca en su proyecto Java, fuertemente recomiendo probarlo! Guava se ha convertido rápidamente en una de las librerías de terceros libres más populares y útiles para Java, como SO compañeros usuarios están de acuerdo. (Si es nuevo en esto, hay algunos excelentes recursos de aprendizaje detrás de ese enlace).


Actualización (2015): Como para Java 8, bueno, todavía usaría el enfoque de Guava porque es mucho más limpio que cualquier otra cosa. Si no quieres la dependencia de Guava, considera un método simple viejo init. El truco con matriz bidimensional y API Stream es bastante feo si me preguntas, y se pone más feo si necesitas crear un mapa cuyas claves y valores no sean del mismo tipo (como Map<Integer, String> en la pregunta).

En cuanto al futuro de la guayaba en general, con respecto a Java 8, Louis Wasserman dijo esto en 2014, y [actualizar] en 2016 se anunció que Guava 21 requerirá y soportará Java 8.


Actualización (2016): Como Tagir Valeev señala, Java 9 finalmente hará que esto esté limpio sin usar nada más que puro JDK, al agregar métodos de fábrica de conveniencia para colecciones:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

370
2018-02-03 21:40



Yo usaría:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. evita la clase anónima, que personalmente considero un mal estilo, y evita
  2. hace que la creación del mapa sea más explícita
  3. hace que el mapa no se pueda modificar
  4. como MY_MAP es constante, lo nombraría como constante

160
2017-07-15 21:29



Java 5 proporciona esta sintaxis más compacta:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

157
2018-02-03 15:44



Una ventaja del segundo método es que puedes envolverlo con Collections.unmodifiableMap() para garantizar que nada va a actualizar la colección más tarde:

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

80
2017-09-14 00:44



Aquí hay un inicializador de mapas estáticos Java 8 de una línea:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

Editar: para inicializar un Map<Integer, String> como en la pregunta, necesitarías algo como esto:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

Editar (2): hay una versión mejorada, de tipo mixto, de i_am_zero que usa una secuencia de new SimpleEntry<>(k, v) llamadas. Mira esa respuesta: https://stackoverflow.com/a/37384773/3950982


52
2017-12-29 10:07



En Java 9:

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

Ver JEP 269 para detalles. JDK 9 alcanzado Disponibilidad general en septiembre de 2017.


39
2017-12-18 23:10



Con Colecciones Eclipse (antes Colecciones GS), todo lo siguiente funcionará:

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

También puede inicializar estáticamente mapas primitivos con colecciones de Eclipse.

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
} 

Nota: Soy un committer para las colecciones de Eclipse


27
2018-02-03 15:55