recorrer - treemap en java ejemplos




¿Cómo puedo inicializar un mapa estático? (20)

¿Cómo inicializarías un Map 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 los 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");
        }
    };
}

Java 8 (solución aseada)

Podemos crear un flujo de entradas de mapa. Ya tenemos dos implementaciones de Entry en java.util.AbstractMap que son SimpleEntry y SimpleImmutableEntry . Para este ejemplo podemos hacer uso de la anterior como:

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));

Para Java 9 también podemos utilizar Map.of como sugiere Tagir en su respuesta here .


Aquí está mi favorito cuando no quiero (o no puedo) usar ImmutableMap.of() Guava, o si necesito un Map mutable:

public static <A> Map<String, A> asMap(Object... keysAndValues) {
    return new LinkedHashMap<String, A>() {{
        for (int i = 0; i < keysAndValues.length - 1; i++) {
            put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
        }
    }};
}

Es muy compacto e ignora los valores extraviados (es decir, una clave final sin un valor).

Uso:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

Como de costumbre, apache-commons tiene el método apropiado MapUtils.putAll (Mapa, Objeto []) :

Por ejemplo, para crear un mapa de color:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

Con Eclipse Collections (anteriormente GS Collections ), 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 comendador de Eclipse Collections.


Debido a que Java no es compatible con los literales de mapas, las instancias de mapas siempre deben crearse y crear instancias explícitas.

Afortunadamente, es posible aproximar el comportamiento de los literales de mapas en Java utilizando métodos de fábrica .

Por ejemplo:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

Salida:

{a = 1, b = 2, c = 3}

Es mucho más conveniente que crear y poblar el mapa un elemento a la vez.


El inicializador de instancia es solo azúcar sintáctica en este caso, ¿verdad? No veo por qué necesita una clase anónima adicional solo para inicializar. Y no funcionará si la clase que se crea 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);
    }
}

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

La clase anónima que estás creando funciona bien. Sin embargo, debe tener en cuenta que esta es una clase interna y, como tal, contendrá una referencia a la instancia de la clase circundante. Así que verás que no puedes hacer ciertas cosas con él (usando XStream para uno). Obtendrás algunos errores muy extraños.

Habiendo dicho eso, siempre que sepas, este enfoque está bien. Lo uso la mayor parte del tiempo para inicializar todo tipo de colecciones de manera concisa.

EDITAR: Señaló correctamente en los comentarios que esta es una clase estática. Obviamente no leí esto lo suficientemente cerca. Sin embargo, mis comentarios todavía se aplican a clases internas anónimas.


Me gusta la forma Guava 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 métodos de fábrica convenientes en ImmutableMap ).

Si desea que el mapa tenga más de 5 entradas, ya no puede usar ImmutableMap.of() . En su lugar, intente ImmutableMap.builder() siguiendo 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 recopilación inmutables de Guava, consulte Colecciones inmutables explicadas en la Guía del usuario de Guava .

(Un subconjunto de) Guayaba solía llamarse Google Collections . Si aún no está utilizando esta biblioteca en su proyecto Java, ¡ le recomiendo probarlo! La guayaba se ha convertido rápidamente en una de las librerías de terceros gratuitas más populares y útiles para Java, según coinciden otros usuarios de SO . (Si eres nuevo en esto, hay algunos recursos de aprendizaje excelentes detrás de ese enlace).

Actualización (2015) : En cuanto a Java 8 , bueno, todavía usaría el enfoque de Guava porque es mucho más limpio que cualquier otra cosa. Si no desea la dependencia de la guayaba, considere un método de inicio antiguo simple . El hack con matriz bidimensional y Stream API es bastante feo si me lo preguntas, y se vuelve 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 Guava en general, con respecto a Java 8, Louis Wasserman dijo esto en 2014, y [ actualización ] en 2016 se anunció que Guava 21 requerirá y apoyará adecuadamente a Java 8 .

Actualización (2016) : Como señala Tagir Valeev , Java 9 finalmente lo hará todo para que no use nada más que JDK puro, agregando métodos de fábrica de conveniencia para las colecciones:

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

No he visto el enfoque que utilizo (y me ha gustado) publicado en ninguna respuesta, así que aquí está:

No me gusta usar inicializadores estáticos porque son torpes, y no me gustan las clases anónimas porque está creando una nueva clase para cada instancia.

en su lugar, prefiero la inicialización que se ve así:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

desafortunadamente, estos métodos no forman parte de la biblioteca estándar de Java, por lo que deberá crear (o usar) una biblioteca de utilidad que defina los siguientes métodos:

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(puede usar 'importar estática' para evitar tener que prefijar el nombre del método)

Me pareció útil proporcionar métodos estáticos similares para las otras colecciones (list, set, sortedSet, sortedMap, etc.)

No es tan agradable como la inicialización del objeto json, pero es un paso en esa dirección, en lo que respecta a la legibilidad.


Nunca crearía una subclase anónima en esta situación. Los inicializadores estáticos funcionan igual de bien, si desea que el mapa no se pueda modificar, por ejemplo:

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

Podría sugerir fuertemente el estilo de "inicialización de doble refuerzo" sobre el estilo de bloque estático.

Alguien puede comentar que no les gustan las clases anónimas, los gastos generales, el rendimiento, etc.

Pero lo que más considero es la legibilidad y mantenibilidad del código. En este punto de vista, sostengo que un doble corchete es un mejor estilo de código en lugar del método estático.

  1. Los elementos están anidados y en línea.
  2. Es más OO, no procesal.
  3. El impacto en el rendimiento es muy pequeño y podría ignorarse.
  4. Mejor soporte de esquema IDE (en lugar de muchos bloques estáticos {} anónimos)
  5. Guardaste algunas líneas de comentario para traerles relación.
  6. Evitar posibles pérdidas de elementos / derivaciones de instancias de objetos no inicializados desde la excepción y el optimizador de bytecode.
  7. No te preocupes por el orden de ejecución del bloque estático.

Además, si conoce el GC de la clase anónima, siempre puede convertirlo en un HashMap normal usando un new HashMap(Map map) .

Puedes hacer esto hasta que te encuentres con otro problema. Si lo hace, debe usar otro estilo de codificación completo (por ejemplo, no estático, clase de fábrica) para ello.


Puedes usar StickyMap y MapEntry de Cactoos :

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

Se cree que su segundo enfoque (inicialización de doble refuerzo) es un patrón anti , por lo que me gustaría ir para el primer enfoque.

Otra forma fácil de inicializar un mapa estático es mediante el uso de esta función de utilidad:

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

    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 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

Nota: en Java 9 puedes usar Map.of


Si desea un mapa no modificable, finalmente java 9 agregó un método of fábrica genial para la interfaz del Map . Método similar se agrega a Set, List también.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


Tal vez sea interesante revisar Google Collections , por ejemplo, los videos que tienen en su página. Proporcionan varias formas de inicializar mapas y conjuntos, y también proporcionan colecciones inmutables.

Actualización: esta biblioteca ahora se llama Guava .


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 evito
  2. Hace más explícita la creación de mapas.
  3. hace que el mapa no sea modificable
  4. como MY_MAP es constante, lo llamaría constante

JEP 269 proporciona algunos métodos de fábrica de conveniencia para colecciones API. Estos métodos de fábrica no están en la versión actual de Java, que es 8, pero están planeados para el lanzamiento de Java 9.

Para Map hay dos métodos de fábrica: of y ofEntries . Usando, puedes pasar pares alternativos de clave / valor. Por ejemplo, para crear un Map como {age: 27, major: cs} :

Map<String, Object> info = Map.of("age", 27, "major", "cs");

Actualmente hay diez versiones sobrecargadas para of, por lo que puede crear un mapa que contenga diez pares clave / valor. Si no te gusta esta limitación o alternar clave / valores, puedes usar ofEntries:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

Ambos ofy ofEntriesdevolverán un valor inmutable Map, por lo que no puedes cambiar sus elementos después de la construcción. Puede probar estas funciones utilizando JDK 9 Early Access .


He leído las respuestas y decidí escribir mi propio creador de mapas. Siéntase libre de copiar y pegar y disfrutar.

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

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> of(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

EDITAR: Últimamente, sigo encontrando el método estático público con ofbastante frecuencia y me gusta. Lo agregué al código e hice que el constructor fuera privado, cambiando así al patrón de método estático de fábrica.


Si solo necesita agregar un valor al mapa, puede usar Collections.singletonMap :

Map<K, V> map = Collections.singletonMap(key, value)




idiomatic