Wie würden Sie eine Statik Map
in Java initialisieren ?
Methode eins: statischer Initialisierer
Methode zwei: Instanzinitialisierer (anonyme Unterklasse) oder eine andere Methode?
Was sind die Vor- und Nachteile von jedem?
Hier ist ein Beispiel, das die beiden Methoden veranschaulicht:
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
, überprüfen Sie stackoverflow.com/a/37384773/1216775Antworten:
Der Instanzinitialisierer ist in diesem Fall nur syntaktischer Zucker, oder? Ich verstehe nicht, warum Sie eine zusätzliche anonyme Klasse benötigen, um sie zu initialisieren. Und es wird nicht funktionieren, wenn die erstellte Klasse endgültig ist.
Sie können eine unveränderliche Karte auch mit einem statischen Initialisierer erstellen:
quelle
Ich mag die Guave- Methode zum Initialisieren einer statischen, unveränderlichen Karte:
Wie Sie sehen können, ist es sehr prägnant (aufgrund der praktischen Factory-Methoden in
ImmutableMap
).Wenn die Karte mehr als 5 Einträge enthalten soll, können Sie sie nicht mehr verwenden
ImmutableMap.of()
. Versuchen Sie stattdessen FolgendesImmutableMap.builder()
:Weitere Informationen zu den Vorteilen der Dienstprogramme für unveränderliche Sammlungen in Guava finden Sie unter Erläuterungen zu unveränderlichen Sammlungen im Guava-Benutzerhandbuch .
(Eine Untergruppe von) Guave hieß früher Google Collections . Wenn Sie diese Bibliothek noch nicht in Ihrem Java-Projekt verwenden, empfehle ich dringend , sie auszuprobieren! Guava hat sich schnell zu einer der beliebtesten und nützlichsten kostenlosen Bibliotheken von Drittanbietern für Java entwickelt, da andere SO-Benutzer zustimmen . (Wenn Sie neu darin sind, steckt hinter diesem Link einige hervorragende Lernressourcen.)
Update (2015) : Was Java 8 betrifft, würde ich immer noch den Guava-Ansatz verwenden, da er viel sauberer ist als alles andere. Wenn Sie keine Guava-Abhängigkeit wünschen, ziehen Sie eine einfache alte Init-Methode in Betracht . Der Hack mit zweidimensionalem Array und Stream-API ist ziemlich hässlich, wenn Sie mich fragen, und wird hässlicher, wenn Sie eine Map erstellen müssen, deren Schlüssel und Werte nicht vom gleichen Typ sind (wie
Map<Integer, String>
in der Frage).Was die Zukunft von Guava im Allgemeinen in Bezug auf Java 8 betrifft, sagte Louis Wasserman dies bereits 2014, und [ Update ] 2016 wurde angekündigt, dass Guava 21 Java 8 benötigen und ordnungsgemäß unterstützen wird .
Update (2016) : Wie Tagir Valeev hervorhebt, wird Java 9 dies endlich sauber machen, indem nur reines JDK verwendet wird, indem praktische Factory-Methoden für Sammlungen hinzugefügt werden :
quelle
Ich würde ... benutzen:
quelle
Java 5 bietet diese kompaktere Syntax:
quelle
HashMap implements Serializable
. Da Sie mit diesem "Trick" tatsächlich eine Unterklasse von HashMap erstellen, erstellen Sie implizit eine serialisierbare Klasse. Und dafür sollten Sie eine serialUID angeben.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
ist in einer beliebigen Unterklasse von Map definiertAbstractMap
und funktioniert in dieser , sodass dies hier kein Problem darstellt. Die Sache mit dem Diamantenoperator ist ärgerlich, wurde aber wie erwähnt jetzt behoben.Ein Vorteil der zweiten Methode besteht darin, dass Sie sie umschließen können, um sicherzustellen
Collections.unmodifiableMap()
, dass die Sammlung später nicht mehr aktualisiert wird:quelle
Hier ist ein einzeiliger statischer Java 8-Karteninitialisierer:
Bearbeiten: Um ein
Map<Integer, String>
wie in der Frage zu initialisieren , benötigen Sie ungefähr Folgendes:Bearbeiten (2): Es gibt eine bessere Version von i_am_zero, die für gemischte Typen geeignet ist und einen Anrufstrom verwendet
new SimpleEntry<>(k, v)
. Überprüfen Sie diese Antwort: https://stackoverflow.com/a/37384773/3950982quelle
String[][]
nicht,Object[][]
wird benötigt). IMHO, dieser Ansatz ist hässlich (noch mehr bei den Darstellern) und schwer zu merken; würde es nicht selbst benutzen.Map.of
in Java 9+Siehe JEP 269 für Details. JDK 9 erreichte im September 2017 die allgemeine Verfügbarkeit .
quelle
Map.ofEntries
Java 9
Wir können
Map.ofEntries
Call verwendenMap.entry( k , v )
, um jeden Eintrag zu erstellen.Wir können auch verwenden,
Map.of
wie von Tagir in seiner Antwort hier vorgeschlagen, aber wir können nicht mehr als 10 Einträge verwendenMap.of
.Java 8 (ordentliche Lösung)
Wir können einen Stream von Karteneinträgen erstellen. Wir haben bereits zwei Implementierungen
Entry
injava.util.AbstractMap
denen SimpleEntry und SimpleImmutableEntry . Für dieses Beispiel können wir erstere verwenden als:quelle
new SimpleEntry<>()
Weg ist weit weniger lesbar als statischput()
: /Mit Eclipse Collections funktionieren alle folgenden Funktionen:
Sie können primitive Karten auch statisch mit Eclipse-Sammlungen initialisieren.
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen
quelle
Ich würde in dieser Situation niemals eine anonyme Unterklasse erstellen. Statische Initialisierer funktionieren genauso gut, wenn Sie die Karte beispielsweise nicht ändern möchten:
quelle
Vielleicht ist es interessant, sich Google Collections anzusehen , z. B. die Videos, die sie auf ihrer Seite haben. Sie bieten verschiedene Möglichkeiten zum Initialisieren von Karten und Sets sowie unveränderliche Sammlungen.
Update: Diese Bibliothek heißt jetzt Guava .
quelle
Ich mag anonymen Unterricht, weil es einfach ist, damit umzugehen:
quelle
Wenn wir mehr als eine Konstante deklarieren, wird dieser Code in einen statischen Block geschrieben und ist in Zukunft schwer zu pflegen. Es ist also besser, eine anonyme Klasse zu verwenden.
Es wird empfohlen, unmodizableMap für Konstanten zu verwenden, andernfalls kann es nicht als Konstante behandelt werden.
quelle
Ich könnte den Stil der "doppelten Klammerinitialisierung" gegenüber dem statischen Blockstil empfehlen.
Jemand kann kommentieren, dass er anonyme Klasse, Overhead, Leistung usw. nicht mag.
Was ich jedoch mehr berücksichtige, ist die Lesbarkeit und Wartbarkeit des Codes. Unter diesem Gesichtspunkt halte ich eine doppelte Klammer für einen besseren Codestil als eine statische Methode.
Wenn Sie den GC der anonymen Klasse kennen, können Sie ihn jederzeit mithilfe von in eine normale HashMap konvertieren
new HashMap(Map map)
.Sie können dies tun, bis Sie auf ein anderes Problem stoßen. Wenn Sie dies tun, sollten Sie dafür einen anderen Codierungsstil (z. B. keine statische, Factory-Klasse) verwenden.
quelle
Wie üblich hat Apache-Commons die richtige Methode MapUtils.putAll (Map, Object []) :
So erstellen Sie eine Farbkarte:
quelle
Arrays.asMap( ... )
Da leider keine Methode in einfachem Java vorhanden ist, halte ich dies für die beste Lösung. Das Rad neu zu erfinden ist normalerweise albern. Ein sehr kleiner Nachteil ist, dass bei Generika eine ungeprüfte Konvertierung erforderlich ist.SuppressWarnings( unchecked )
in Eclipse mit einer Linie wieMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
String[][]
bekomme ich die "Warnung"! Und das funktioniert natürlich nur, wenn SieK
undV
die gleiche Klasse sind. Ich nehme an, Sie haben in Ihrem Eclipse-Setup (verständlicherweise) "ungeprüfte Konvertierung" nicht auf "Ignorieren" gesetzt.Hier ist mein Favorit, wenn ich Guaven nicht verwenden möchte (oder kann)
ImmutableMap.of()
oder wenn ich eine veränderbare braucheMap
:Es ist sehr kompakt und ignoriert Streuwerte (dh einen endgültigen Schlüssel ohne Wert).
Verwendungszweck:
quelle
Wenn Sie eine nicht modifizierbare Karte möchten, hat Java 9 schließlich eine coole Factory-Methode
of
zurMap
Schnittstelle hinzugefügt . Eine ähnliche Methode wird auch zu Set, List hinzugefügt.Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
quelle
Ich bevorzuge die Verwendung eines statischen Initialisierers, um zu vermeiden, dass anonyme Klassen generiert werden (was keinen weiteren Zweck hätte). Daher werde ich Tipps auflisten, die mit einem statischen Initialisierer initialisiert werden. Alle aufgeführten Lösungen / Tipps sind typsicher.
Hinweis: Die Frage sagt nichts darüber aus, wie die Karte nicht geändert werden kann, daher werde ich das weglassen, aber ich weiß, dass dies leicht möglich ist
Collections.unmodifiableMap(map)
.Erster Tipp
Der erste Tipp ist, dass Sie einen lokalen Verweis auf die Karte erstellen und ihr einen KURZEN Namen geben können:
Zweiter Tipp
Der zweite Tipp ist, dass Sie eine Hilfsmethode zum Hinzufügen von Einträgen erstellen können. Sie können diese Hilfsmethode auch öffentlich machen, wenn Sie:
Die Hilfsmethode kann hier jedoch nicht wiederverwendet werden, da nur Elemente hinzugefügt werden können
myMap2
. Um es wiederverwendbar zu machen, könnten wir die Karte selbst zu einem Parameter der Hilfsmethode machen, aber dann wäre der Initialisierungscode nicht kürzer.Dritter Tipp
Der dritte Tipp ist, dass Sie mit der Auffüllfunktion eine wiederverwendbare Builder-ähnliche Hilfsklasse erstellen können. Dies ist wirklich eine einfache Helferklasse mit 10 Zeilen, die typsicher ist:
quelle
Die anonyme Klasse, die Sie erstellen, funktioniert gut. Sie sollten sich jedoch bewusst sein, dass dies eine innere Klasse ist und als solche einen Verweis auf die umgebende Klasseninstanz enthält. Sie werden also feststellen, dass Sie bestimmte Dinge damit nicht tun können (mit XStream für einen). Sie werden einige sehr seltsame Fehler bekommen.
Abgesehen davon ist dieser Ansatz in Ordnung, solange Sie sich dessen bewusst sind. Ich benutze es die meiste Zeit, um alle Arten von Sammlungen auf prägnante Weise zu initialisieren.
BEARBEITEN: In den Kommentaren wurde korrekt darauf hingewiesen, dass dies eine statische Klasse ist. Offensichtlich habe ich das nicht genau genug gelesen. Meine Kommentare gelten jedoch weiterhin für anonyme innere Klassen.
quelle
Wenn Sie etwas knappes und relativ sicheres möchten, können Sie die Typprüfung zur Kompilierungszeit einfach auf die Laufzeit verschieben:
Diese Implementierung sollte alle Fehler auffangen:
quelle
Mit Java 8 verwende ich das folgende Muster:
Es ist nicht der knappste und etwas umständlichste, aber
java.util
quelle
toMap
Signatur einschließlich eines Kartenanbieters verwendet werden, um den Kartentyp anzugeben.Wenn Sie der Karte nur einen Wert hinzufügen müssen, können Sie Collections.singletonMap verwenden :
quelle
Sie können
StickyMap
undMapEntry
von Cactoos verwenden :quelle
Ihr zweiter Ansatz (Double Brace-Initialisierung) wird als Anti-Pattern angesehen , daher würde ich mich für den ersten Ansatz entscheiden.
Eine andere einfache Möglichkeit, eine statische Karte zu initialisieren, ist die Verwendung dieser Dienstprogrammfunktion:
Hinweis: In können
Java 9
Sie Map.of verwendenquelle
Ich mag die Syntax des statischen Initialisierers nicht und bin von anonymen Unterklassen nicht überzeugt. Im Allgemeinen stimme ich allen Nachteilen der Verwendung statischer Initialisierer und allen Nachteilen der Verwendung anonymer Unterklassen zu, die in den vorherigen Antworten erwähnt wurden. Andererseits reichen mir die in diesen Beiträgen vorgestellten Profis nicht aus. Ich bevorzuge die statische Initialisierungsmethode:
quelle
Ich habe den Ansatz, den ich verwende (und der mir immer besser gefällt), in keiner Antwort gesehen. Hier ist er also:
Ich mag es nicht, statische Initialisierer zu verwenden, weil sie klobig sind, und ich mag keine anonymen Klassen, weil sie für jede Instanz eine neue Klasse erstellen.
Stattdessen bevorzuge ich eine Initialisierung, die folgendermaßen aussieht:
Leider sind diese Methoden nicht Teil der Standard-Java-Bibliothek. Daher müssen Sie eine Dienstprogrammbibliothek erstellen (oder verwenden), die die folgenden Methoden definiert:
(Sie können 'statisch importieren' verwenden, um zu vermeiden, dass der Name der Methode vorangestellt werden muss.)
Ich fand es nützlich, ähnliche statische Methoden für die anderen Sammlungen (list, set, sortedSet, sortedMap usw.) bereitzustellen.
Es ist nicht ganz so schön wie die Initialisierung von JSON-Objekten, aber es ist ein Schritt in diese Richtung, was die Lesbarkeit betrifft.
quelle
Da Java keine Kartenliterale unterstützt, müssen Karteninstanzen immer explizit instanziiert und gefüllt werden.
Glücklicherweise ist es möglich, das Verhalten von Kartenliteralen in Java mithilfe von Factory-Methoden zu approximieren .
Zum Beispiel:
Ausgabe:
Es ist viel praktischer, als die Karte einzeln zu erstellen und zu füllen.
quelle
JEP 269 bietet einige praktische Factory-Methoden für die Sammlungs-API. Diese Factory-Methoden sind nicht in der aktuellen Java-Version 8 enthalten, sondern für die Java 9-Version geplant.
Denn
Map
es gibt zwei Fabrikmethoden:of
undofEntries
. Mitof
können Sie abwechselnd Schlüssel / Wert-Paare übergeben. Zum Beispiel, um einMap
Like zu erstellen{age: 27, major: cs}
:Derzeit gibt es zehn überladene Versionen für
of
, sodass Sie eine Karte mit zehn Schlüssel / Wert-Paaren erstellen können. Wenn Ihnen diese Einschränkung oder alternative Schlüssel / Werte nicht gefallen, können Sie Folgendes verwendenofEntries
:Beide
of
undofEntries
geben eine unveränderliche zurückMap
, so dass Sie ihre Elemente nach der Konstruktion nicht ändern können. Sie können diese Funktionen mit JDK 9 Early Access ausprobieren .quelle
Nun ... ich mag Aufzählungen;)
quelle
Ich habe die Antworten gelesen und beschlossen, meinen eigenen Kartenersteller zu schreiben. Fühlen Sie sich frei zu kopieren, einzufügen und zu genießen.
EDIT: In letzter Zeit finde ich
of
ziemlich oft öffentliche statische Methoden und ich mag es irgendwie. Ich fügte es dem Code hinzu und machte den Konstruktor privat, wodurch ich zum statischen Factory-Methodenmuster wechselte.EDIT2: In jüngerer Zeit mag ich die aufgerufene statische Methode nicht mehr
of
, da sie bei der Verwendung statischer Importe ziemlich schlecht aussieht. Ich habe esmapOf
stattdessen in umbenannt, um es für statische Importe besser geeignet zu machen.quelle