Cum ti-ar inițializa un statice "Harta" în Java?
Metoda una: static initialiser
Metoda două: exemplu initialiser (anonim subclasă)
sau
o alta metoda?
Care sunt argumentele pro și contra de fiecare?
Aici este un exemplu care ilustrează cele două metode:
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");
}
};
}
De exemplu initialiser este doar zahăr sintactic în acest caz, nu? Eu nu't vedea de ce ai nevoie de un plus de clasa anonim, doar pentru a inițializa. Și nu o't de lucru în cazul în care clasa fiind creat este definitivă.
Puteți crea un imuabil hartă folosind un statice initialiser prea:
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);
}
}
Îmi place Guava mod de inițializare statică, imuabilă harta:
static final Map<Integer, String> MY_MAP = ImmutableMap.of(
1, "one",
2, "two"
);
După cum puteți vedea, l's foarte concis (pentru a convenabil fabrică de metode ImmutableMap
).
Daca vrei harta pentru a avea mai mult de 5 intrări, nu mai puteți utiliza ImmutableMap.de()
. În schimb, încercați să ImmutableMap.constructor()
de-a lungul acestor linii:
static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
.put(1, "one")
.put(2, "two")
// ...
.put(15, "fifteen")
.build();
Pentru a afla mai multe despre beneficiile de Guava's imuabil colecție de utilități, a se vedea Imuabil Colecții Explicat în Guava Ghidul Utilizatorului.
(Un subset al) Guava folosit pentru a fi numit Google Colecții. Dacă nu't folosind această bibliotecă în proiect Java totuși, m-am puternic recomandăm să încercați-l! Guava a devenit rapid una dintre cele mai populare și utile gratuite 3rd party libs pentru Java, ca colegii, ASTFEL încât utilizatorii sunt de acord. (Dacă sunteți nou la ea, sunt unele excelente resurse de învățare în spatele link-ul.)
Update (2015): pentru Ca Java 8, ei bine, mi-ar mai folosi Guava abordare pentru că este mult mai curat decât orice altceva. Dacă tu nu't vreau Guava dependență, ia în considerare o plain old metoda init. Hack cu doi-dimensional matrice și Stream API este destul de urât dacă mă întrebi pe mine, și devine urât dacă aveți nevoie pentru a crea o Hartă a cărui chei și valori nu sunt de același tip (cum ar fi Harta<Integer, String>
în întrebare).
Ca pentru viitor de Guava, în general, în ceea ce privește Java 8, Louis Wasserman a spus asta înapoi în 2014, și [actualizare] în 2016 a fost anunțat că Guava 21 va necesita în mod corespunzător și suport Java 8.
Update (2016): Ca Tagir Valeev subliniază, Java 9 va face în cele din urmă acest lucru curat pentru a face folosind nimic, dar pur JDK, adăugând comoditatea metode fabrica pentru colecții:
static final Map<Integer, String> MY_MAP = Map.of(
1, "one",
2, "two"
);
Mi-ar folosi:
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);
}
}
Un avantaj pentru cea de-a doua metodă este că puteți încheia cu Colectiile.unmodifiableMap()` pentru a garanta că nimic nu este de gând să actualizeze colecția de mai târziu:
private static final Map<Integer, String> CONSTANT_MAP =
Collections.unmodifiableMap(new HashMap<Integer, String>() {{
put(1, "one");
put(2, "two");
}});
// later on...
CONSTANT_MAP.put(3, "three"); // going to throw an exception!
Aici's un Java 8 o linie statică harta inițializare:
private static final Map<String, String> EXTENSION_TO_MIMETYPE =
Arrays.stream(new String[][] {
{ "txt", "text/plain" },
{ "html", "text/html" },
{ "js", "application/javascript" },
{ "css", "text/css" },
{ "xml", "application/xml" },
{ "png", "image/png" },
{ "gif", "image/gif" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "svg", "image/svg+xml" },
}).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));
Edit: pentru a inițializa o Harta<Integer, String> ca în întrebare, te'd nevoie de ceva de genul asta:
static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
{1, "one"},
{2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));
Edit(2): Există o mai bună mixte, de tip capabil versiune de i_am_zero care utilizează un flux de noi SimpleEntry<>(k, v)` apeluri. A verifica afară acest răspuns: https://stackoverflow.com/a/37384773/3950982
În Java 9:
private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");
A se vedea JEP 269 pentru detalii. JDK 9-a ajuns disponibilitatea generală în septembrie 2017.
Putem folosi Harta.ofEntries` ca:
import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
entry(1, "one"),
entry(2, "two"),
entry(3, "three"),
entry(4, "four"),
entry(5, "five"),
entry(6, "six"),
entry(7, "seven"),
entry(8, "eight"),
entry(9, "nine"),
entry(10, "ten"));
Putem folosi, de asemenea, Harta lui.de cum a sugerat de către Tagir în răspunsul său aici, dar nu putem avea mai mult de 10 de înregistrări folosind Harta.de`.
Putem crea un Flux de hartă intrări. Avem deja două implementări de "Intrare" în java.util.AbstractMap` care sunt SimpleEntry și SimpleImmutableEntry. Pentru acest exemplu, putem face uz de fostul fi:
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));
Cu Eclipse Colecții, toate din următoarele va funcționa:
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");
}
Puteți, de asemenea, static inițializa primitive hărți cu Eclipse Colecții.
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");
}
Notă: sunt o comiter sau de altcineva pentru Eclipse Colecții
Niciodată nu mi-aș crea un anonim subclasă în această situație. Static de initializare lucra la fel de bine, dacă doriți pentru a face harta unmodifiable de exemplu:
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);
}
Poate l's interesante pentru a verifica Google Colecții, de exemplu, filmele pe care le au pe pagina lor. Ele oferă diferite moduri de a inițializa hărți și apune, și să ofere imuabil colecții, precum și.
Update: Această bibliotecă este acum numit Guava.
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);
}
}
Dacă vom declara mai mult de o constantă, atunci codul va fi scris în bloc static și că este greu pentru a menține în viitor. Deci, este mai bine să utilizați clasa anonim.
public class Test {
public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
{
put(1, "one");
put(2, "two");
}
});
}
Și este recomandat a se folosit unmodifiableMap pentru constante se poate't fi tratate ca fiind constantă.
Aș putea să sugerez "dublu bretele de inițializare" stil bloc static de stil.
Cineva ar putea comenta că ei don't ca anonim clasă, de regie, de performanță, etc.
Dar asta nu mai ia în considerare este codul de lizibilitate și mentenabilitatea. Din acest punct de vedere, eu stau un dublu bretele este un cod mai bun stil mai degrabă decât metoda statică.
În plus, știți GC din clasa anonim, puteți converti întotdeauna la un normal HashMap, prin utilizarea new HashMap(Map)`.
Puteți face acest lucru până când te-ai confruntat cu o altă problemă. Dacă o faci, ar trebui să utilizați un alt complet de stil de codificare (de exemplu, nu static, clasa fabrică) pentru asta.
Ca de obicei apache commons-a buna metoda MapUtils.putAll(Harta<K, V>, Object[]):
De exemplu, pentru a crea o hartă de culoare:
Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}
});
Dacă doriți unmodifiable hartă, în cele din urmă java 9 adăugat o metodă fabrica " de " la "Harta" interfață. Metodă similară este adăugat pentru a Seta, Lista la fel de bine.
Harta<String, String> unmodifiableMap = Hartă.de("cheie1", "valoare1", "key2", "valoare2");
Aici's preferatul meu atunci când eu nu't vrea (sau nu poate) să utilizeze Guava's ImmutableMap.de()
, sau daca am nevoie de un mutabil "Harta":
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]);
}
}};
}
L's foarte compact, și ignoră fără stăpân valori (de exemplu, un final cheie fără valoare).
Utilizare:
Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
Eu prefer să folosesc o inițializare statică pentru a evita generarea de anonim clase (care nu mai au nici un scop), așa că am'll lista de sfaturi inițializarea cu o inițializare statică. Toate enumerate de solutii / sfaturi sunt de tip în condiții de siguranță.
Notă: întrebarea nu't spune nimic despre a face harta unmodifiable, asa ca va las, dar știu că acesta poate fi foarte usor de facut cu Colectiile.unmodifiableMap(map)`.
Primul sfat
1 sfat este că puteți face un local de referință la hartă și a vă da un nume SCURT:
private static final Map<Integer, String> myMap = new HashMap<>();
static {
final Map<Integer, String> m = myMap; // Use short name!
m.put(1, "one"); // Here referencing the local variable which is also faster!
m.put(2, "two");
m.put(3, "three");
}
Al doilea sfat
Al 2-lea sfat este că puteți crea un helper metodă de a adăuga intrări; de asemenea, puteți face acest ajutor metodă publice, dacă doriți să:
private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
p(1, "one"); // Calling the helper method.
p(2, "two");
p(3, "three");
}
private static void p(Integer k, String v) {
myMap2.put(k, v);
}
Helper metodă de aici nu este re-utilizabile pentru că, deși se poate doar adăuga elemente la myMap2
. Pentru a face re-utilizabile, am putea face harta în sine un parametru de metoda de ajutor, dar apoi, cod de inițializare nu ar fi mai scurt.
Al treilea sfat
Al 3-lea sfat este că puteți crea o re-utilizabile constructor-ca helper class cu popularea funcționalitate. Acest lucru este foarte simplu, 10-line helper class care este de tip în condiții de siguranță:
public class Test {
private static final Map<Integer, String> myMap3 = new HashMap<>();
static {
new B<>(myMap3) // Instantiating the helper class with our map
.p(1, "one")
.p(2, "two")
.p(3, "three");
}
}
class B<K, V> {
private final Map<K, V> m;
public B(Map<K, V> m) {
this.m = m;
}
public B<K, V> p(K k, V v) {
m.put(k, v);
return this; // Return this for chaining
}
}
Anonim clasa're crearea funcționează bine. Cu toate acestea ar trebui să fie conștienți de faptul că aceasta este o interior clasă și, ca atare, l'll să conțină o referire la cei din jur instanță de clasă. Deci'll găsi puteți't face anumite lucruri cu ea (folosind XStream pentru unul). Te'll obține ceva foarte ciudat erori.
Având în a spus că, atât timp cât're conștienți atunci această abordare este în regulă. Eu folosesc cel mai mult din timp pentru inițializarea tot felul de colecții într-o formă concisă de moda.
EDIT: a Subliniat în mod corect în comentarii că aceasta este o clasa static. Evident, eu am't citit acest lucru suficient de atent. Cu toate acestea, comentariile mele nu se aplică în continuare pentru clase interioare anonime.
Daca vrei ceva concis și relativ în siguranță, puteți schimba compilare-timp de tip verificare la run-time:
static final Map<String, Integer> map = MapUtils.unmodifiableMap(
String.class, Integer.class,
"cat", 4,
"dog", 2,
"frog", 17
);
Această punere în aplicare ar trebui să prindă orice erori:
import java.util.HashMap;
public abstract class MapUtils
{
private MapUtils() { }
public static <K, V> HashMap<K, V> unmodifiableMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
return Collections.<K, V>unmodifiableMap(makeMap(
keyClazz,
valClazz,
keyValues));
}
public static <K, V> HashMap<K, V> makeMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
if (keyValues.length % 2 != 0)
{
throw new IllegalArgumentException(
"'keyValues' was formatted incorrectly! "
+ "(Expected an even length, but found '" + keyValues.length + "')");
}
HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);
for (int i = 0; i < keyValues.length;)
{
K key = cast(keyClazz, keyValues[i], i);
++i;
V val = cast(valClazz, keyValues[i], i);
++i;
result.put(key, val);
}
return result;
}
private static <T> T cast(Class<? extends T> clazz, Object object, int i)
{
try
{
return clazz.cast(object);
}
catch (ClassCastException e)
{
String objectName = (i % 2 == 0) ? "Key" : "Value";
String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
}
}
}