Guava nos proporciona excelentes métodos de fábrica para tipos de Java, como Maps.newHashMap()
.
¿Pero también hay constructores para Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Guava nos proporciona excelentes métodos de fábrica para tipos de Java, como Maps.newHashMap()
.
¿Pero también hay constructores para Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Respuestas:
Dado que la Map
interfaz de Java 9 contiene:
Map.of(k1,v1, k2,v2, ..)
Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)
. Las limitaciones de esos métodos de fábrica son que:
null
s como claves y / o valores (si necesita almacenar nulos, eche un vistazo a otras respuestas)Si necesitamos un mapa mutable (como HashMap), podemos usar su constructor de copia y dejar que copie el contenido del mapa creado a través deMap.of(..)
Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );
null
valores, lo que puede ser un problema según el caso de uso.
Map.of(k1,v1, k2,v2, ...)
se puede utilizar de forma segura cuando no tenemos muchos valores. Para una mayor cantidad de valores, Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
nos da un código más legible que es menos propenso a errores (a menos que te haya entendido mal).
No existe tal cosa para HashMaps, pero puede crear un ImmutableMap con un constructor:
final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build();
Y si necesita un mapa mutable, puede simplemente enviarlo al constructor HashMap.
final Map<String, Integer> m = Maps.newHashMap(
ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build());
ImmutableMap
no admite null
valores. Por lo tanto, hay una limitación de este enfoque: no puede establecer valores en su HashMap
to null
.
new HashMap
constructor de Java en lugar del Maps.newHashMap
método estático ?
No es un constructor, pero usa un inicializador:
Map<String, String> map = new HashMap<String, String>() {{
put("a", "1");
put("b", "2");
}};
map instanceof HashMap
falso? Parece una idea no tan buena.
map.getClass()==HashMap.class
devolverá falso. Pero esa es una prueba estúpida de todos modos. HashMap.class.isInstance(map)
debería ser preferido, y eso volverá verdadero.
Esto es similar a la respuesta aceptada, pero un poco más limpia, en mi opinión:
ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);
Hay varias variaciones del método anterior y son excelentes para hacer mapas estáticos, inmutables e inmutables.
Aquí hay uno muy simple ...
public class FluentHashMap<K, V> extends java.util.HashMap<K, V> {
public FluentHashMap<K, V> with(K key, V value) {
put(key, value);
return this;
}
public static <K, V> FluentHashMap<K, V> map(K key, V value) {
return new FluentHashMap<K, V>().with(key, value);
}
}
luego
import static FluentHashMap.map;
HashMap<String, Integer> m = map("a", 1).with("b", 2);
Ver https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065
Un simple constructor de mapas es trivial de escribir:
public class Maps {
public static <Q,W> MapWrapper<Q,W> map(Q q, W w) {
return new MapWrapper<Q, W>(q, w);
}
public static final class MapWrapper<Q,W> {
private final HashMap<Q,W> map;
public MapWrapper(Q q, W w) {
map = new HashMap<Q, W>();
map.put(q, w);
}
public MapWrapper<Q,W> map(Q q, W w) {
map.put(q, w);
return this;
}
public Map<Q,W> getMap() {
return map;
}
}
public static void main(String[] args) {
Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3).getMap();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
Puedes usar:
HashMap<String,Integer> m = Maps.newHashMap(
ImmutableMap.of("a",1,"b",2)
);
No es tan elegante y legible, pero funciona.
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
, ¿mejor?
HashMap
es mutable; no hay necesidad de constructor.
Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
ImmutableSet
. Si realmente desea que sea mutable, puede inicializarlo en el constructor o en un bloque inicializador de instancia o en un bloque inicializador estático si es un campo estático.
ImmutableMap
eso obviamente.
{{init();}}
no estática (no en el constructor, ya que otro constructor podría olvidarlo). Y es bueno que sea una especie de acción atómica. Si el mapa es volátil, al inicializarlo con un constructor, asegúrese de que siempre esté null
o en el estado final, nunca a la mitad.
Puede utilizar la API fluida en Eclipse Collections :
Map<String, Integer> map = Maps.mutable.<String, Integer>empty()
.withKeyValue("a", 1)
.withKeyValue("b", 2);
Assert.assertEquals(Maps.mutable.with("a", 1, "b", 2), map);
Aquí hay un blog con más detalles y ejemplos.
Nota: Soy un comprometido con las colecciones de Eclipse.
Hace un tiempo tuve un requisito similar. No tiene nada que ver con Guava, pero puedes hacer algo como esto para poder construir limpiamente Map
usando un constructor fluido.
Cree una clase base que amplíe Map.
public class FluentHashMap<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 4857340227048063855L;
public FluentHashMap() {}
public FluentHashMap<K, V> delete(Object key) {
this.remove(key);
return this;
}
}
Luego, cree el constructor fluido con métodos que se adapten a sus necesidades:
public class ValueMap extends FluentHashMap<String, Object> {
private static final long serialVersionUID = 1L;
public ValueMap() {}
public ValueMap withValue(String key, String val) {
super.put(key, val);
return this;
}
... Add withXYZ to suit...
}
Luego puede implementarlo así:
ValueMap map = new ValueMap()
.withValue("key 1", "value 1")
.withValue("key 2", "value 2")
.withValue("key 3", "value 3")
Esto es algo que siempre quise, especialmente al configurar dispositivos de prueba. Finalmente, decidí escribir un constructor sencillo y fluido propio que pudiera construir cualquier implementación de mapa: https://gist.github.com/samshu/b471f5a2925fa9d9b718795d8bbdfe42#file-mapbuilder-java
/**
* @param mapClass Any {@link Map} implementation type. e.g., HashMap.class
*/
public static <K, V> MapBuilder<K, V> builder(@SuppressWarnings("rawtypes") Class<? extends Map> mapClass)
throws InstantiationException,
IllegalAccessException {
return new MapBuilder<K, V>(mapClass);
}
public MapBuilder<K, V> put(K key, V value) {
map.put(key, value);
return this;
}
public Map<K, V> build() {
return map;
}
Aquí hay uno que escribí
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class MapBuilder<K, V> {
private final Map<K, V> map;
/**
* Create a HashMap builder
*/
public MapBuilder() {
map = new HashMap<>();
}
/**
* Create a HashMap builder
* @param initialCapacity
*/
public MapBuilder(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* Create a Map builder
* @param mapFactory
*/
public MapBuilder(Supplier<Map<K, V>> mapFactory) {
map = mapFactory.get();
}
public MapBuilder<K, V> put(K key, V value) {
map.put(key, value);
return this;
}
public Map<K, V> build() {
return map;
}
/**
* Returns an unmodifiable Map. Strictly speaking, the Map is not immutable because any code with a reference to
* the builder could mutate it.
*
* @return
*/
public Map<K, V> buildUnmodifiable() {
return Collections.unmodifiableMap(map);
}
}
Lo usas así:
Map<String, Object> map = new MapBuilder<String, Object>(LinkedHashMap::new)
.put("event_type", newEvent.getType())
.put("app_package_name", newEvent.getPackageName())
.put("activity", newEvent.getActivity())
.build();
Usando java 8:
Este es un enfoque de Java-9 Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
public class MapUtil {
import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
private MapUtil() {}
@SafeVarargs
public static Map<String, Object> ofEntries(SimpleEntry<String, Object>... values) {
return Stream.of(values).collect(toMap(Entry::getKey, Entry::getValue));
}
public static SimpleEntry<String, Object> entry(String key, Object value) {
return new SimpleEntry<String, Object>(key, value);
}
}
Cómo utilizar:
import static your.package.name.MapUtil.*;
import java.util.Map;
Map<String, Object> map = ofEntries(
entry("id", 1),
entry("description", "xyz"),
entry("value", 1.05),
entry("enable", true)
);
Underscore-java puede crear hashmap.
Map<String, Object> value = U.objectBuilder()
.add("firstName", "John")
.add("lastName", "Smith")
.add("age", 25)
.add("address", U.arrayBuilder()
.add(U.objectBuilder()
.add("streetAddress", "21 2nd Street")
.add("city", "New York")
.add("state", "NY")
.add("postalCode", "10021")))
.add("phoneNumber", U.arrayBuilder()
.add(U.objectBuilder()
.add("type", "home")
.add("number", "212 555-1234"))
.add(U.objectBuilder()
.add("type", "fax")
.add("number", "646 555-4567")))
.build();
// {firstName=John, lastName=Smith, age=25, address=[{streetAddress=21 2nd Street,
// city=New York, state=NY, postalCode=10021}], phoneNumber=[{type=home, number=212 555-1234},
// {type=fax, number=646 555-4567}]}