¿Cómo inicializarías una estática Map
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<>();
static {
myMap.put(1, "one");
myMap.put(2, "two");
}
private static final Map<Integer, String> myMap2 = new HashMap<>(){
{
put(1, "one");
put(2, "two");
}
};
}
Map.of
elseMap.ofEntries
, consulte stackoverflow.com/a/37384773/1216775Respuestas:
El iniciador de instancia es solo azúcar sintáctico 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 está creando es final.
También puede crear un mapa inmutable utilizando un inicializador estático:
fuente
Me gusta la forma de guayaba de inicializar un mapa estático e inmutable:
Como puede ver, es muy conciso (debido a los convenientes métodos de fábrica
ImmutableMap
).Si desea que el mapa tenga más de 5 entradas, ya no puede usarlo
ImmutableMap.of()
. En su lugar, intenteImmutableMap.builder()
siguiendo estas líneas:Para obtener más información sobre los beneficios de las utilidades de colección inmutables de Guava, consulte Colecciones inmutables explicadas en la Guía del usuario de Guava .
(Un subconjunto de) La guayaba solía llamarse Google Collections . Si no está utilizando esta biblioteca en su proyecto de Java, sin embargo, yo fuertemente recomiendo probarlo! La guayaba se ha convertido rápidamente en una de las librerías gratuitas de terceros más populares y útiles para Java, como coinciden otros usuarios de SO . (Si eres nuevo en él, hay algunos excelentes recursos de aprendizaje 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 Guava, considere un método init simple y antiguo . El hack con matriz bidimensional y Stream API es bastante feo si me 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 admitirá Java 8 de manera adecuada .
Actualización (2016) : como señala Tagir Valeev , Java 9 finalmente lo hará limpio usando nada más que JDK puro, al agregar métodos de fábrica convenientes para colecciones:
fuente
Yo usaría:
fuente
Java 5 proporciona esta sintaxis más compacta:
fuente
HashMap implements Serializable
. Como en realidad crea una subclase de HashMap usando este "truco", está creando implícitamente una clase Serializable. Y para esto, debe proporcionar un número de serie.Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.
- IntelliJHashMap.equals
se defineAbstractMap
y funciona en cualquier subclase de Map, por lo que no es una preocupación aquí Lo del operador de diamantes es molesto, pero como se mencionó ahora se ha resuelto.Una ventaja del segundo método es que puede envolverlo
Collections.unmodifiableMap()
para garantizar que nada va a actualizar la colección más tarde:fuente
Aquí hay un inicializador de mapa estático de una línea Java 8:
Editar: para inicializar un
Map<Integer, String>
como en la pregunta, necesitarías algo como esto:Editar (2): i_am_zero tiene una mejor versión de tipo mixto que utiliza un flujo de
new SimpleEntry<>(k, v)
llamadas. Mira esa respuesta: https://stackoverflow.com/a/37384773/3950982fuente
String[][]
lo que no seráObject[][]
necesario). En mi humilde opinión, este enfoque es feo (aún más con los moldes) y difícil de recordar; No lo usaría yo mismo.Map.of
en Java 9+Ver JEP 269 para más detalles. JDK 9 alcanzó disponibilidad general en septiembre de 2017.
fuente
Map.ofEntries
Java 9
Podemos usar
Map.ofEntries
, llamandoMap.entry( k , v )
para crear cada entrada.También podemos usar
Map.of
como lo sugiere Tagir en su respuesta aquí, pero no podemos tener más de 10 entradas usandoMap.of
.Java 8 (solución ordenada)
Podemos crear un flujo de entradas de mapa. Ya tenemos dos implementaciones de
Entry
enjava.util.AbstractMap
que son SimpleEntry y SimpleImmutableEntry . Para este ejemplo podemos hacer uso del anterior como:fuente
new SimpleEntry<>()
camino es mucho menos legible que estáticoput()
: /Con Eclipse Collections , funcionará todo lo siguiente:
También puede inicializar estáticamente mapas primitivos con Eclipse Collections.
Nota: Soy un committer para Eclipse Collections
fuente
Nunca crearía una subclase anónima en esta situación. Los inicializadores estáticos funcionan igualmente bien, si desea que el mapa no se pueda modificar, por ejemplo:
fuente
Tal vez sea interesante ver Colecciones de Google , 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 .
fuente
Me gusta la clase anónima, porque es fácil de manejar:
fuente
Si declaramos más de una constante, ese código se escribirá en bloque estático y eso es difícil de mantener en el futuro. Por lo tanto, es mejor usar una clase anónima.
Y se sugiere utilizar un mapa no modificable para constantes; de lo contrario, no se puede tratar como constante.
fuente
Podría sugerir fuertemente el estilo de "inicialización de doble paréntesis" sobre el estilo de bloque estático.
Alguien puede comentar que no les gusta la clase anónima, los gastos generales, el rendimiento, etc.
Pero lo que más considero es la legibilidad y facilidad de mantenimiento del código. En este punto de vista, creo que una doble llave es un mejor estilo de código en lugar de un método estático.
Además, si conoce el GC de la clase anónima, siempre puede convertirlo en un HashMap normal mediante el uso
new HashMap(Map map)
.Puede hacer esto hasta que enfrente otro problema. Si lo hace, debe usar otro estilo de codificación completo (por ejemplo, no estático, clase de fábrica) para ello.
fuente
Como de costumbre, apache-commons tiene el método apropiado MapUtils.putAll (Map, Object []) :
Por ejemplo, para crear un mapa de colores:
fuente
Arrays.asMap( ... )
en Java simple, creo que esta es la mejor solución. Reinventar la rueda suele ser una tontería. Un inconveniente muy leve es que con los genéricos necesitará una conversión sin control.SuppressWarnings( unchecked )
en Eclipse con una línea comoMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
String[][]
recibo la "advertencia"! Y, por supuesto, eso solo funciona si tuK
yV
eres de la misma clase. ¿Supongo que no ha (comprensiblemente) configurado "conversión no verificada" en "Ignorar" en su configuración de Eclipse?Este es mi favorito cuando no quiero (o no puedo) usar Guava's
ImmutableMap.of()
, o si necesito un mutableMap
:Es muy compacto e ignora los valores perdidos (es decir, una clave final sin un valor).
Uso:
fuente
Si desea un mapa no modificable, finalmente Java 9 agregó un método de fábrica genial
of
para laMap
interfaz. Se agrega un método similar a Set, List también.Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
fuente
Prefiero usar un inicializador estático para evitar generar clases anónimas (que no tendrían ningún otro propósito), por lo que enumeraré sugerencias para inicializar con un inicializador estático. Todas las soluciones / consejos enumerados son de tipo seguro.
Nota: La pregunta no dice nada acerca de hacer que el mapa no se pueda modificar, por lo que lo dejaré fuera, pero sé que se puede hacer fácilmente
Collections.unmodifiableMap(map)
.Primer consejo
El primer consejo es que puedes hacer una referencia local al mapa y darle un nombre CORTO:
Segundo consejo
El segundo consejo es que puede crear un método auxiliar para agregar entradas; También puede hacer público este método auxiliar si desea:
El método de ayuda aquí no es reutilizable porque solo puede agregar elementos a
myMap2
. Para que sea reutilizable, podríamos convertir el mapa en sí mismo en un parámetro del método auxiliar, pero el código de inicialización no sería más corto.Tercer consejo
El tercer consejo es que puede crear una clase de ayuda reutilizable tipo constructor con la funcionalidad de completar. Esta es realmente una clase auxiliar simple de 10 líneas que es de tipo seguro:
fuente
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 clase circundante. Entonces descubrirá que no puede hacer ciertas cosas con él (usando XStream para uno). Obtendrás algunos errores muy extraños.
Habiendo dicho eso, siempre que lo 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 de cerca. Sin embargo mis comentarios no se siguen aplicando a las clases internas anónimas.
fuente
Si desea algo conciso y relativamente seguro, puede cambiar la verificación del tipo de tiempo de compilación al tiempo de ejecución:
Esta implementación debería detectar cualquier error:
fuente
Con Java 8, he llegado a usar el siguiente patrón:
No es lo más conciso y un poco indirecto, pero
java.util
fuente
toMap
firma, incluido un proveedor de mapas, para especificar el tipo de mapa.Si solo necesita agregar un valor al mapa, puede usar Collections.singletonMap :
fuente
Puede usar
StickyMap
yMapEntry
desde Cactoos :fuente
Se cree que su segundo enfoque (inicialización de doble paréntesis) es un antipatrón , por lo que elegiría el primer enfoque.
Otra forma fácil de inicializar un mapa estático es mediante el uso de esta función de utilidad:
Nota: en
Java 9
puedes usar Map.offuente
No me gusta la sintaxis del inicializador estático y no estoy convencido de las subclases anónimas. En general, estoy de acuerdo con todas las desventajas de usar inicializadores estáticos y todas las desventajas de usar subclases anónimas que se mencionaron en respuestas anteriores. Por otro lado, los profesionales presentados en estas publicaciones no son suficientes para mí. Prefiero usar el método de inicialización estática:
fuente
No he visto el enfoque que uso (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 cambio, prefiero una inicialización que se vea así:
desafortunadamente, estos métodos no son parte de la biblioteca estándar de Java, por lo que deberá crear (o usar) una biblioteca de utilidades que defina los siguientes métodos:
(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 bueno como la inicialización de objetos json, pero es un paso en esa dirección, en lo que respecta a la legibilidad.
fuente
Debido a que Java no admite literales de mapas, las instancias de mapas siempre se deben instanciar y completar explícitamente.
Afortunadamente, es posible aproximar el comportamiento de los literales de mapas en Java utilizando métodos de fábrica .
Por ejemplo:
Salida:
Es mucho más conveniente que crear y llenar el mapa de un elemento a la vez.
fuente
JEP 269 proporciona algunos métodos de fábrica convenientes para la API de colecciones. Estos métodos de fábrica no están en la versión actual de Java, que es la 8, pero están planificados para el lanzamiento de Java 9.
Porque
Map
hay dos métodos de fábrica:of
yofEntries
. Usandoof
, puede pasar pares clave / valor alternativos. Por ejemplo, para crear un meMap
gusta{age: 27, major: cs}
:Actualmente hay diez versiones sobrecargadas
of
, por lo que puede crear un mapa que contenga diez pares clave / valor. Si no le gusta esta limitación o la alternancia de clave / valores, puede usarofEntries
:Ambos
of
yofEntries
devolverán un inmutableMap
, por lo que no puede cambiar sus elementos después de la construcción. Puede probar estas funciones con JDK 9 Early Access .fuente
Bueno ... me gustan las enumeraciones;)
fuente
He leído las respuestas y decidí escribir mi propio creador de mapas. Siéntase libre de copiar y pegar y disfrutar.
EDITAR: Últimamente, sigo encontrando métodos estáticos públicos con
of
bastante frecuencia y me gusta un poco. Lo agregué al código e hice privado al constructor, cambiando así al patrón de método de fábrica estático.EDIT2: Incluso más recientemente, ya no me gusta el método estático llamado
of
, ya que se ve bastante mal cuando uso importaciones estáticas. En sumapOf
lugar, le cambié el nombre, lo que lo hace más adecuado para las importaciones estáticas.fuente