Guava bietet uns großartige Factory-Methoden für Java-Typen, wie z Maps.newHashMap()
.
Aber gibt es auch Builder für Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Guava bietet uns großartige Factory-Methoden für Java-Typen, wie z Maps.newHashMap()
.
Aber gibt es auch Builder für Java Maps?
HashMap<String,Integer> m = Maps.BuildHashMap.
put("a",1).
put("b",2).
build();
Antworten:
Da Java 9- Map
Schnittstelle enthält:
Map.of(k1,v1, k2,v2, ..)
Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)
. Einschränkungen dieser Fabrikmethoden sind, dass sie:
null
s nicht als Schlüssel und / oder Werte halten (wenn Sie Nullen speichern müssen, schauen Sie sich andere Antworten an)Wenn wir eine veränderbare Karte benötigen (wie HashMap), können wir ihren Kopierkonstruktor verwenden und den Inhalt der über erstellten Karte kopieren lassenMap.of(..)
Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );
null
Werte zulassen , was je nach Anwendungsfall ein Problem sein kann.
Map.of(k1,v1, k2,v2, ...)
kann sicher verwendet werden, wenn wir nicht viele Werte haben. Bei einer größeren Anzahl von Werten erhalten Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)
wir besser lesbaren Code, der weniger fehleranfällig ist (es sei denn, ich habe Sie falsch verstanden).
Für HashMaps gibt es so etwas nicht, aber Sie können eine ImmutableMap mit einem Builder erstellen:
final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build();
Und wenn Sie eine veränderbare Map benötigen, können Sie diese einfach dem HashMap-Konstruktor zuführen.
final Map<String, Integer> m = Maps.newHashMap(
ImmutableMap.<String, Integer>builder().
put("a", 1).
put("b", 2).
build());
ImmutableMap
unterstützt keine null
Werte. Daher gibt es eine Einschränkung dieses Ansatzes: Sie können keine Werte in Ihrem HashMap
to festlegen null
.
new HashMap
Java-Konstruktor anstelle der statischen Maps.newHashMap
Methode zu verwenden?
Nicht ganz ein Builder, aber mit einem Initialisierer:
Map<String, String> map = new HashMap<String, String>() {{
put("a", "1");
put("b", "2");
}};
map instanceof HashMap
falsch machen ? Sieht nach einer nicht so tollen Idee aus.
map.getClass()==HashMap.class
gibt false zurück. Aber das ist trotzdem ein dummer Test. HashMap.class.isInstance(map)
sollte bevorzugt werden, und das wird wahr zurückgeben.
Dies ähnelt der akzeptierten Antwort, ist aber meiner Ansicht nach etwas sauberer:
ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);
Es gibt verschiedene Variationen der oben genannten Methode, und sie eignen sich hervorragend zum Erstellen statischer, unveränderlicher und unveränderlicher Karten.
Hier ist eine sehr einfache ...
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);
}
}
dann
import static FluentHashMap.map;
HashMap<String, Integer> m = map("a", 1).with("b", 2);
Siehe https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065
Ein einfacher Kartenersteller ist trivial zu schreiben:
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());
}
}
}
Sie können verwenden:
HashMap<String,Integer> m = Maps.newHashMap(
ImmutableMap.of("a",1,"b",2)
);
Es ist nicht so edel und lesbar, macht aber die Arbeit.
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
, besser?
HashMap
ist veränderlich; Es ist kein Baumeister erforderlich.
Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
ImmutableSet
. Wenn Sie wirklich möchten, dass es veränderbar ist, können Sie es im Konstruktor oder in einem Instanzinitialisierungsblock oder einem statischen Initialisierungsblock initialisieren, wenn es sich um ein statisches Feld handelt.
ImmutableMap
dort offensichtlich sagen sollen .
{{init();}}
(nicht im Konstruktor, da andere Konstruktoren dies möglicherweise vergessen). Und es ist schön, dass es eine Art atomare Aktion ist. Wenn die Karte flüchtig ist, stellen Sie bei der Initialisierung mit einem Builder sicher, dass sie immer entweder null
oder im Endzustand ist und niemals zur Hälfte gefüllt ist.
Sie können die fließende API in Eclipse-Sammlungen verwenden :
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);
Hier ist ein Blog mit mehr Details und Beispielen.
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.
Ich hatte vor einiger Zeit eine ähnliche Anforderung. Es hat nichts mit Guave zu tun, aber Sie können so etwas tun, um eine sauber zu konstruierenMap
mit einem fließenden Builder eine .
Erstellen Sie eine Basisklasse, die Map erweitert.
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;
}
}
Erstellen Sie dann den fließenden Builder mit Methoden, die Ihren Anforderungen entsprechen:
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...
}
Sie können es dann folgendermaßen implementieren:
ValueMap map = new ValueMap()
.withValue("key 1", "value 1")
.withValue("key 2", "value 2")
.withValue("key 3", "value 3")
Das wollte ich schon immer, besonders beim Einrichten von Testgeräten. Schließlich entschied ich mich, einen einfachen, fließenden Builder zu schreiben, der jede Map-Implementierung erstellen kann - 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;
}
Hier ist eine, die ich geschrieben habe
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);
}
}
Sie verwenden es so:
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();
Verwenden von Java 8:
Dies ist ein Ansatz von 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);
}
}
Wie benutzt man:
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)
);
Unterstrich-Java kann Hashmap erstellen.
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}]}