Guava ci fornisce ottimi metodi di fabbrica per i tipi Java, come Maps.newHashMap()
.
Ma esistono anche builder per Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Guava ci fornisce ottimi metodi di fabbrica per i tipi Java, come Maps.newHashMap()
.
Ma esistono anche builder per Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Risposte:
Poiché l' Map
interfaccia Java 9 contiene:
Map.of(k1,v1, k2,v2, ..)
Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)
. I limiti di questi metodi di fabbrica sono che:
null
s come chiavi e / o valori (se devi memorizzare valori nulli dai un'occhiata alle altre risposte)Se abbiamo bisogno di una mappa mutabile (come HashMap) possiamo usare il suo costruttore di copia e lasciarlo copiare il contenuto della mappa creata tramiteMap.of(..)
Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );
null
valori, il che può essere un problema a seconda del caso d'uso.
Map.of(k1,v1, k2,v2, ...)
può essere utilizzato in sicurezza quando non abbiamo molti valori. Per quantità maggiori di valori Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
ci fornisce un codice più leggibile che è meno soggetto a errori (a meno che non ti abbia frainteso).
Non esiste nulla di simile per HashMaps, ma puoi creare una ImmutableMap con un builder:
final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build();
E se hai bisogno di una mappa modificabile, puoi semplicemente fornirla al costruttore HashMap.
final Map<String, Integer> m = Maps.newHashMap(
ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build());
ImmutableMap
non supporta i null
valori. Quindi c'è una limitazione di questo approccio: non puoi impostare valori nel tuo HashMap
to null
.
new HashMap
costruttore Java invece del Maps.newHashMap
metodo statico ?
Non proprio un builder, ma usando un inizializzatore:
Map<String, String> map = new HashMap<String, String>() {{
put("a", "1");
put("b", "2");
}};
map instanceof HashMap
falso? Sembra un'idea non così eccezionale.
map.getClass()==HashMap.class
restituirà false. Ma è comunque un test stupido. HashMap.class.isInstance(map)
dovrebbe essere preferito e ciò restituirà vero.
Questo è simile alla risposta accettata, ma un po 'più pulita, a mio avviso:
ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);
Esistono diverse varianti del metodo precedente e sono ottime per creare mappe statiche, immutabili e immutabili.
Eccone uno molto semplice ...
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);
}
}
poi
import static FluentHashMap.map;
HashMap<String, Integer> m = map("a", 1).with("b", 2);
Vedi https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065
Un semplice generatore di mappe è banale da scrivere:
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());
}
}
}
Puoi usare:
HashMap<String,Integer> m = Maps.newHashMap(
ImmutableMap.of("a",1,"b",2)
);
Non è così elegante e leggibile, ma fa il lavoro.
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
, meglio?
HashMap
è mutevole; non c'è bisogno di un costruttore.
Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
ImmutableSet
. Se vuoi davvero che sia modificabile, puoi inizializzarlo nel costruttore o in un blocco inizializzatore di istanza o in un blocco inizializzatore statico se è un campo statico.
ImmutableMap
ovviamente.
{{init();}}
(non nel costruttore, poiché un altro costruttore potrebbe dimenticarlo). Ed è bello che sia una specie di azione atomica. Se la mappa è volatile, inizializzarla con un builder assicurarsi che sia sempre null
o nello stato finale, mai riempita a metà.
Puoi utilizzare l'API fluente nelle raccolte Eclipse :
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);
Ecco un blog con maggiori dettagli ed esempi.
Nota: sono un committer per le collezioni Eclipse.
Qualche tempo fa avevo un requisito simile. Non ha nulla a che fare con Guava ma puoi fare qualcosa del genere per essere in grado di costruire in modo pulito Map
un costruttore fluente.
Crea una classe base che estenda 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;
}
}
Quindi crea il costruttore fluente con metodi adatti alle tue esigenze:
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...
}
Puoi quindi implementarlo in questo modo:
ValueMap map = new ValueMap()
.withValue("key 1", "value 1")
.withValue("key 2", "value 2")
.withValue("key 3", "value 3")
Questo è qualcosa che ho sempre desiderato, soprattutto durante la configurazione dei dispositivi di prova. Alla fine, ho deciso di scrivere un mio semplice builder fluente in grado di costruire qualsiasi implementazione di Map - 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;
}
Eccone uno che ho scritto
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 usi in questo modo:
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();
Utilizzando java 8:
Questo è un approccio di 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);
}
}
Come usare:
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 può creare 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}]}