it-swarm.com.de

Wie kann ich eine statische Karte initialisieren?

Wie würden Sie eine statische Map in Java initialisieren?

Methode 1: statischer Initialisierer 
Methode 2: Instanzinitialisierer (anonyme Unterklasse) Oder Eine andere Methode?

Was sind die Vor- und Nachteile eines jeden?

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<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");
        }
    };
}
997
dogbane

Der Instanzeninitialisierer ist in diesem Fall nur syntaktischer Zucker, oder? Ich kann nicht verstehen, warum Sie eine zusätzliche anonyme Klasse nur zum Initialisieren benötigen. Und es wird nicht funktionieren, wenn die Klasse, die erstellt wird, endgültig ist.

Sie können eine unveränderliche Karte auch mit einem statischen Initialisierer erstellen:

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);
    }
}
1008

Ich mag die Guava Methode zum Initialisieren einer statischen, unveränderlichen Karte:

_static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);
_

Wie Sie sehen, ist es sehr übersichtlich (aufgrund der praktischen Factory-Methoden in ImmutableMapNAME _ ).

Wenn die Map mehr als 5 Einträge haben soll, können Sie ImmutableMap.of() nicht mehr verwenden. Versuchen Sie stattdessen ImmutableMap.builder() in dieser Richtung:

_static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();
_

Weitere Informationen zu den Vorteilen der Dienstprogramme für unveränderliche Sammlungen in Guava finden Sie unter Erklärte unveränderliche Sammlungen im Guava-Benutzerhandbuch .

(Eine Untergruppe von) Guava hieß früher Google Collections . Wenn Sie diese Bibliothek noch nicht in Ihrem Java Projekt verwenden, empfehle ich nachdrücklich, sie auszuprobieren! Guava hat sich schnell zu einer der beliebtesten und nützlichsten kostenlosen Drittanbieter-Bibliotheken für Java entwickelt, da fellow SO Benutzer stimmen z . (Wenn Sie neu darin sind, gibt es einige ausgezeichnete Lernressourcen hinter diesem Link.)


pdate (2015): Bezüglich Java 8 würde ich immer noch den Guava-Ansatz verwenden, da er viel sauberer ist als alles andere. Wenn Sie keine Guava-Abhängigkeit möchten, ziehen Sie eine einfache alte Init-Methode in Betracht. Der Hack mit zweidimensionales 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 selben Typ sind (wie _Map<Integer, String>_ in der Frage).

Was die Zukunft von Guava im Allgemeinen betrifft, in Bezug auf Java 8, Louis Wasserman sagte dies im Jahr 2014 und [ Update ] im Jahr 2016 wurde angekündigt, dass Guava 21 wird erfordern und ordnungsgemäß unterstützen Java 8 .


pdate (2016): Wie Tagir Valeev weist darauf hin , Java 9 macht dies endlich sauber, indem es nichts anderes als reines JDK verwendet, indem es Folgendes hinzufügt: Convenience-Factory-Methoden für Sammlungen:

_static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);
_
420
Jonik

Ich würde ... benutzen:

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);
    }
}
  1. es vermeidet anonyme Kurse, die ich persönlich für einen schlechten Stil halte, und vermeide es
  2. dadurch wird die Erstellung von Karten deutlicher
  3. es macht die Karte unveränderlich
  4. da MY_MAP konstant ist, würde ich es als konstant bezeichnen
169

Java 5 bietet diese kompaktere Syntax:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};
164
Chris Noe

Ein Vorteil der zweiten Methode ist, dass Sie sie mit Collections.unmodifiableMap() umschließen können, um sicherzustellen, dass die Sammlung später nicht aktualisiert wird:

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!
88

Hier ist ein einzeiliger Java 8-Karteninitialisierer:

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]));

Bearbeiten: Um einen Map<Integer, String> wie in der Frage zu initialisieren, benötigen Sie etwa Folgendes:

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): Es gibt eine bessere, gemischte typfähige Version von i_am_zero, die einen Stream von new SimpleEntry<>(k, v)-Aufrufen verwendet. Überprüfen Sie diese Antwort: https://stackoverflow.com/a/37384773/3950982

59
Luke Hutchison

In Java 9:

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

Siehe JEP 269 für Details. JDK 9 erreicht allgemeine Verfügbarkeit im September 2017. 

47
Tagir Valeev

Mit Eclipse Collections funktionieren alle folgenden Funktionen:

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");
}

Sie können auch primitive Karten mit Eclipse-Sammlungen statisch initialisieren.

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");
} 

Hinweis: Ich bin ein Committer für Eclipse Collections 

30
Donald Raab

Java 9

Wir können Map.ofEntries verwenden als:

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"));

Wir können auch Map.of verwenden, wie von Tagir in seiner Antwort hier vorgeschlagen, aber wir können nicht mehr als 10 Einträge mit Map.of verwenden.

Java 8 (saubere Lösung)

Wir können einen Stream von Karteneinträgen erstellen. Wir haben bereits zwei Implementierungen von Entry in Java.util.AbstractMap, nämlich SimpleEntry und SimpleImmutableEntry . Für dieses Beispiel können wir ehemalige verwenden:

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));
27
i_am_zero

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:

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);
}
26
eljenso

Vielleicht ist es interessant, Google Collections auszuprobieren, z. 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 .

16
Kaarel

Ich mag anonyme Kurse, weil es einfach ist, damit umzugehen:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});
16
Shushant
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);
    }
}

Wenn wir mehr als eine Konstante deklarieren, wird dieser Code in einen statischen Block geschrieben, der in Zukunft schwer zu verwalten ist. Daher ist es besser, eine anonyme Klasse zu verwenden.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

Und es wird empfohlen, unmodifiableMap für Konstanten zu verwenden, da es sonst nicht als konstant betrachtet werden kann.

11

Ich könnte den Stil der "doppelten Klammer-Initialisierung" gegenüber dem statischen Blockstil stark vorschlagen.

Jemand kann sagen, dass er keine anonymen Kurse, Zusatzkosten, Leistung usw. mag.

Ich denke jedoch mehr an die Lesbarkeit und Wartbarkeit des Codes. In dieser Hinsicht halte ich eine doppelte Klammer für einen besseren Codestil als für eine statische Methode.

  1. Die Elemente sind verschachtelt und inline.
  2. Es ist mehr OO, nicht prozedural.
  3. die Auswirkungen auf die Leistung sind wirklich gering und könnten ignoriert werden.
  4. Bessere IDE Gliederungsunterstützung (statt vieler anonymer statischer {} Blöcke)
  5. Sie haben ein paar Kommentarzeilen gespeichert, um eine Beziehung herzustellen.
  6. Verhindern Sie, dass ein Element-Leck/Instanz-Lead eines nicht initialisierten Objekts vom Ausnahme- und Bytecode-Optimierer ausgeschlossen wird.
  7. Keine Sorge über die Reihenfolge der Ausführung des statischen Blocks.

Wenn Sie die GC der anonymen Klasse kennen, können Sie sie außerdem mithilfe von new HashMap(Map map) in eine normale HashMap konvertieren.

Sie können dies tun, bis Sie ein anderes Problem haben. In diesem Fall sollten Sie einen anderen Codierungsstil verwenden (z. B. keine statische, Werksklasse).

10
Dennis C

Wie üblich hat Apache-Commons die richtige Methode MapUtils.putAll (Map, Object []) :

So erstellen Sie beispielsweise eine Farbkarte: 

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });
8
agad

Hier ist mein Favorit, wenn ich Guavas ImmutableMap.of() nicht verwenden möchte (kann oder kann) oder wenn ich eine veränderliche Map benötige:

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]);
        }
    }};
}

Es ist sehr kompakt und ignoriert verirrte Werte (d. H. Einen endgültigen Schlüssel ohne Wert).

Verwendungszweck:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
7
neu242

Wenn Sie eine nicht modifizierbare Karte haben möchten, haben Sie Java 9 eine coole Factory-Methode of zu Map hinzugefügt. Eine ähnliche Methode wurde auch zu Set, List hinzugefügt.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");

7
Bharanidharan K

Ich bevorzuge die Verwendung eines statischen Initialisierers, um zu vermeiden, anonyme Klassen zu generieren (was keinen weiteren Zweck hätte), daher liste ich die Tipps auf, die mit einem statischen Initialisierer initialisiert werden. Alle aufgeführten Lösungen/Tipps sind typsicher.

Hinweis: Die Frage sagt nichts darüber aus, dass die Karte nicht verändert werden kann, also werde ich das weglassen, aber ich weiß, dass dies leicht mit Collections.unmodifiableMap(map) erledigt werden kann.

Erster Tipp

Der erste Tipp besteht darin, dass Sie einen lokalen Verweis auf die Karte erstellen und dieser einen KURZEN Namen geben können:

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");
}

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 Folgendes möchten:

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);
}

Die Hilfsmethode ist hier jedoch nicht wiederverwendbar, da sie nur myMap2 Elemente hinzufügen kann. Um es wiederverwendbar zu machen, könnten wir die Map selbst zu einem Parameter der Hilfsmethode machen, der Initialisierungscode wäre jedoch nicht kürzer. 

Dritter Tipp

Der dritte Tipp ist, dass Sie mit der Populationsfunktion eine wiederverwendbare Builder-like-Helper-Klasse erstellen können. Dies ist wirklich eine einfache 10-Zeilen-Helper-Klasse, die typsicher ist:

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
    }
}
5
icza

Die anonyme Klasse, die Sie erstellen, funktioniert gut. Sie sollten jedoch wissen, dass es sich um eine inner -Klasse handelt, und als solche enthält sie einen Verweis auf die umgebende Klasseninstanz. Sie werden also feststellen, dass Sie bestimmte Dinge damit nicht machen können (verwenden Sie XStream für eins). Sie erhalten einige sehr seltsame Fehler.

Solange Sie wissen, ist dieser Ansatz in Ordnung. Ich benutze es meistens, um alle Arten von Kollektionen prägnant zu initialisieren. 

BEARBEITEN: In den Kommentaren wurde korrekt darauf hingewiesen, dass dies eine statische Klasse ist. Natürlich habe ich das nicht genau genug gelesen. Meine Kommentare do gelten jedoch weiterhin für anonyme innere Klassen.

5
Brian Agnew

Wenn Sie etwas knappes und relativ sicheres wollen, können Sie die Typprüfung der Kompilierzeit einfach zur Laufzeit verschieben:

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

Diese Implementierung sollte alle Fehler abfangen:

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);
        }
    }
}
4
Philip

Sie können StickyMap und MapEntry aus Cactoos verwenden:

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);
4
yegor256

Mit Java 8 habe ich folgendes Muster verwendet:

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Es ist nicht das knappste und ein bisschen Kreisverkehr, aber

  • außerhalb von Java.util ist nichts erforderlich
  • es ist typsicher und kann problemlos verschiedene Typen für Schlüssel und Wert aufnehmen. 
4
zrvan

Ihr zweiter Ansatz (Double-Brace-Initialisierung) wird als Anti-Pattern betrachtet. Ich würde also den ersten Ansatz wählen.

Eine weitere einfache Möglichkeit zum Initialisieren einer statischen Map ist die Verwendung dieser Dienstprogrammfunktion:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

Hinweis: In Java 9 können Sie Map.of verwenden.

3
R. Oosterholt

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 mit den Methodenfactoryzu approximieren. 

Zum Beispiel:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

Ausgabe:

{a = 1, b = 2, c = 3}

Dies ist viel bequemer als das Erstellen und Füllen der Karte auf einmal.

3
nazar_art

Ich habe nicht den Ansatz gesehen, den ich verwende (und gewachsen bin) und in allen Antworten gepostet habe, daher hier:

Ich mag es nicht, statische Initialisierer zu verwenden, weil sie unübersichtlich sind, Und ich mag keine anonymen Klassen, weil für jede Instanz eine neue Klasse erstellt wird.

stattdessen bevorzuge ich eine Initialisierung, die folgendermaßen aussieht:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

leider sind diese Methoden nicht Bestandteil der Java-Standardbibliothek Sie müssen daher eine Dienstprogrammbibliothek erstellen (oder verwenden), die die folgenden Methoden definiert:

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(Sie können 'statische importieren' verwenden, um zu vermeiden, dass der Methodenname vorangestellt wird.)

Ich fand es nützlich, ähnliche statische Methoden für die anderen Sammlungen bereitzustellen (list, set, sortiertesSet, sortierte Karte usw.).

Es ist nicht ganz so schön wie die Initialisierung von Json-Objekten, aber es ist ein Schritt in diese Richtung, was die Lesbarkeit angeht.

3
josh

Ich mag keine statische Initialisierungssyntax und bin nicht überzeugt, anonyme Unterklassen zu erhalten. Im Allgemeinen stimme ich allen Nachteilen der Verwendung von statischen Initialisierern und allen Nachteilen der Verwendung von anonymen Unterklassen zu, die in früheren Antworten erwähnt wurden. Andererseits reichen mir die in diesen Beiträgen vorgestellten Profis nicht aus. Ich bevorzuge die statische Initialisierungsmethode:

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}
3

Wenn Sie nur einen Wert zur Karte hinzufügen müssen, können Sie Collections.singletonMap verwenden: 

Map<K, V> map = Collections.singletonMap(key, value)
2
Stromata

Ich habe die Antworten gelesen und beschloss, meinen eigenen Map Builder zu schreiben. Fühlen Sie sich frei zu kopieren und zu genießen.

import Java.util.Collections;
import Java.util.HashMap;
import Java.util.Map;

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

EDIT: In letzter Zeit finde ich immer öfter öffentliche statische Methoden of und ich mag es irgendwie. Ich fügte es in den Code ein und machte den Konstruktor privat, wodurch ich zu einem statischen Factory-Method-Pattern wechselte.

EDIT2: Auch in letzter Zeit mag ich keine statische Methode mit dem Namen of mehr, da sie bei statischen Importen ziemlich schlecht aussieht. Ich habe es stattdessen in mapOf umbenannt, wodurch es für statische Importe besser geeignet ist.

2
Vlasec

JEP 269 bietet einige bequeme Factory-Methoden für die Collections-API. Diese werkseitigen Methoden befinden sich nicht in der aktuellen Java-Version (8), sondern sind für Java 9 Release geplant. 

Für Map gibt es zwei Factory-Methoden: of und ofEntries. Mit of können Sie alternierende Schlüssel/Wert-Paare übergeben. Zum Beispiel, um eine Map wie {age: 27, major: cs} zu erstellen:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

Momentan gibt es zehn überladene Versionen für of, sodass Sie eine Map erstellen können, die zehn Schlüssel/Wert-Paare enthält. Wenn Sie diese Einschränkung oder alternierende Schlüssel/Werte nicht mögen, können Sie ofEntries verwenden:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

Sowohl of als auch ofEntries geben eine unveränderliche Map zurück, sodass Sie ihre Elemente nach der Konstruktion nicht ändern können. Sie können diese Funktionen mit JDK 9 Early Access ausprobieren.

2
Ali Dehghani

Nun ... ich mag Enums;)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}
2
jglatre

Dieser verwendet Apache commons-lang, das sich höchstwahrscheinlich bereits auf Ihrem Klassenpfad befindet:

Map<String, String> collect = Stream.of(
        Pair.of("hello", "world"),
        Pair.of("abc", "123"),
        Pair.of("Java", "eight")
).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
1
egridasov

In Java 8 kann der prozedurale Ansatz auch in Supplier eingeschlossen werden:

Map<String,String> m = ((Supplier<Map<String,String>>)(() -> {
    Map<String,String> result = new HashMap<>();
    result.put("foo","hoo");
    ...
    return result;
)).get();

Es ist nur ein hypothetischer Weg, kann aber nützlich sein, wenn Sie wirklich Einzeiler benötigen.

0

Selbst mit Guavas Nice ImmutableMap-Klasse möchte ich manchmal eine veränderliche Karte fließend erstellen. Als ich mir vornahm, statische Blockierungen und die anonyme Untertypsache zu vermeiden, schrieb ich, als Java 8 kam, eine kleine Bibliothek, die als Fluent bezeichnet wurde.

// simple usage, assuming someMap is a Map<String, String> already declared
Map<String, String> example = new Fluent.HashMap<String, String>()
    .append("key1", "val1")
    .append("key2", "val2")
    .appendAll(someMap);

Mit der Standardeinstellung für die Java 8-Schnittstelle konnte ich die Fluent.Map-Methoden für alle Standard-Java-Map-Implementierungen (z. B. HashMap, ConcurrentSkipListMap usw.) ohne langwierige Wiederholung implementieren.

Unveränderbare Karten sind auch einfach.

Map<String, Integer> immutable = new Fluent.LinkedHashMap<String, Integer>()
    .append("one", 1)
    .append("two", 2)
    .append("three", 3)
    .unmodifiable();

Siehe https://github.com/alexheretic/fluent für Quelle, Dokumentation und Beispiele.

0
Alex Butler

Jetzt, da Java 8 heraus ist, ist diese Frage ein erneutes Aufsuchen. Ich habe einen Versuch unternommen - es sieht so aus, als könnten Sie die Lambda-Ausdruckssyntax nutzen, um eine recht nette und prägnante (aber typsichere) Map-Literal-Syntax zu erhalten, die folgendermaßen aussieht:

        Map<String,Object> myMap = hashMap(
                bob -> 5,
                TheGimp -> 8,
                incredibleKoolAid -> "James Taylor",
                heyArnold -> new Date()
        );

        Map<String,Integer> typesafeMap = treeMap(
                a -> 5,
                bee -> 8,
                sea -> 13
                deep -> 21
        );

Ungeprüfter Beispielcode unter https://Gist.github.com/galdosd/10823529 Ich wäre neugierig auf die Meinungen anderer (dies ist leicht übel ...).

0
Domingo Ignacio

Wenn Sie eine String-Darstellung Ihrer Daten verwenden können, ist dies auch eine Option in Java 8:

static Map<Integer, String> MAP = Stream.of(
        "1=one",
        "2=two"
).collect(Collectors.toMap(k -> Integer.parseInt(k.split("=")[0]), v -> v.split("=")[1]));
0

Ich mag die statische Initialisierungsmethode "verwenden", wenn ich eine abstrakte Klasse konkret verwirklicht habe, die zwar einen initialisierenden Konstruktor, aber keinen Standardkonstruktor definiert hat, aber meine Unterklasse einen Standardkonstruktor haben soll.

Zum Beispiel:

public abstract class Shape {

    public static final String COLOR_KEY = "color_key";
    public static final String OPAQUE_KEY = "opaque_key";

    private final String color;
    private final Boolean opaque;

    /**
     * Initializing constructor - note no default constructor.
     *
     * @param properties a collection of Shape properties
     */
    public Shape(Map<String, Object> properties) {
        color = ((String) properties.getOrDefault(COLOR_KEY, "black"));
        opaque = (Boolean) properties.getOrDefault(OPAQUE_KEY, false);
    }

    /**
     * Color property accessor method.
     *
     * @return the color of this Shape
     */
    public String getColor() {
        return color;
    }

    /**
     * Opaque property accessor method.
     *
     * @return true if this Shape is opaque, false otherwise
     */
    public Boolean isOpaque() {
        return opaque;
    }
}

und meine konkrete Umsetzung dieser Klasse - aber sie will/benötigt einen Standardkonstruktor:

public class SquareShapeImpl extends Shape {

    private static final Map<String, Object> DEFAULT_PROPS = new HashMap<>();

    static {
        DEFAULT_PROPS.put(Shape.COLOR_KEY, "yellow");
        DEFAULT_PROPS.put(Shape.OPAQUE_KEY, false);
    }

    /**
     * Default constructor -- intializes this square to be a translucent yellow
     */
    public SquareShapeImpl() {
        // the static initializer was useful here because the call to 
        // this(...) must be the first statement in this constructor
        // i.e., we can't be mucking around and creating a map here
        this(DEFAULT_PROPS);
    }

    /**
     * Initializing constructor -- create a Square with the given
     * collection of properties.
     *
     * @param props a collection of properties for this SquareShapeImpl
     */
    public SquareShapeImpl(Map<String, Object> props) {
        super(props);
    }
}

um diesen Standardkonstruktor zu verwenden, machen wir einfach:

public class StaticInitDemo {

    public static void main(String[] args) {

        // create a translucent, yellow square...
        Shape defaultSquare = new SquareShapeImpl();

        // etc...
    }
}
0
Jim Daehn

Ich mag die anonyme Klassensyntax. es ist nur weniger Code. Ein großer Nachteil ist jedoch, dass Sie das Objekt nicht per Remoting serialisieren können. Sie erhalten eine Ausnahme, wenn Sie die anonyme Klasse nicht auf der Remote-Seite finden können.

0
Chase Seibert

Hier ist der Code von AbacusUtil

Map<Integer, String> map = N.asMap(1, "one", 2, "two");
// Or for Immutable map 
ImmutableMap<Integer, String> = ImmutableMap.of(1, "one", 2, "two");

Erklärung: Ich bin der Entwickler von AbacusUtil.

0
user_3380739

Anmerkung: Diese Antwort gehört eigentlich zu Frage Wie initialisiere ich eine HashMap direkt (in wörtlicher Form)? aber da es als [Duplikat] dieses gekennzeichnet ist ...


Vor Java 9 mit seiner Map.of () (die ebenfalls auf 10 Mappings beschränkt ist) können Sie eine Map-Implementierung Ihrer Wahl erweitern, z.

public class InitHashMap<K, V> extends HashMap<K, V>

HashMap Konstruktoren neu implementieren:

public InitHashMap() {
    super();
}

public InitHashMap( int initialCapacity, float loadFactor ) {
    super( initialCapacity, loadFactor );
}

public InitHashMap( int initialCapacity ) {
    super( initialCapacity );
}

public InitHashMap( Map<? extends K, ? extends V> m ) {
    super( m );
}

und fügen Sie einen zusätzlichen Konstruktor hinzu, der von Aerthels Antwort inspiriert ist, aber generisch ist, indem Sie die Typen Object... und <K, V> verwenden:

public InitHashMap( final Object... keyValuePairs ) {

    if ( keyValuePairs.length % 2 != 0 )
        throw new IllegalArgumentException( "Uneven number of arguments." );

    K key = null;
    int i = -1;

    for ( final Object keyOrValue : keyValuePairs )
        switch ( ++i % 2 ) {
            case 0:  // key
                if ( keyOrValue == null )
                    throw new IllegalArgumentException( "Key[" + (i >> 1) + "] is <null>." );
                key = (K) keyOrValue;
                continue;
            case 1:  // value
                put( key, (V) keyOrValue );
        }
}

Lauf

public static void main( final String[] args ) {

    final Map<Integer, String> map = new InitHashMap<>( 1, "First", 2, "Second", 3, "Third" );
    System.out.println( map );
}

Ausgabe

{1=First, 2=Second, 3=Third}

Sie können die Map-Schnittstelle ebenfalls erweitern:

public interface InitMap<K, V> extends Map<K, V> {

    static <K, V> Map<K, V> of( final Object... keyValuePairs ) {

        if ( keyValuePairs.length % 2 != 0 )
            throw new IllegalArgumentException( "Uneven number of arguments." );

        final Map<K, V> map = new HashMap<>( keyValuePairs.length >> 1, .75f );
        K key = null;
        int i = -1;

        for ( final Object keyOrValue : keyValuePairs )
            switch ( ++i % 2 ) {
                case 0: // key
                    if ( keyOrValue == null )
                        throw new IllegalArgumentException( "Key[" + (i >> 1) + "] is <null>." );
                    key = (K) keyOrValue;
                    continue;
                case 1: // value
                    map.put( key, (V) keyOrValue );
            }
        return map;
    }
}

Lauf

public static void main( final String[] args ) {

    System.out.println( InitMap.of( 1, "First", 2, "Second", 3, "Third" ) );
}

Ausgabe

{1=First, 2=Second, 3=Third}
0
Gerold Broser

Ich habe etwas anders gemacht. Nicht das Beste, aber es funktioniert für mich. Vielleicht könnte es "generisiert" werden.

private static final Object[][] ENTRIES =
{
  {new Integer(1), "one"},
  {new Integer(2), "two"},
};
private static final Map myMap = newMap(ENTRIES);

private static Map newMap(Object[][] entries)
{
  Map map = new HashMap();

  for (int x = 0; x < entries.length; x++)
  {
    Object[] entry = entries[x];

    map.put(entry[0], entry[1]);
  }

  return map;
}
0
Gary Kephart

Hier gibt es einige gute Antworten, aber ich möchte noch eine weitere Antwort geben.

Erstellen Sie Ihre eigene statische Methode, um eine Map zu erstellen und zu initialisieren. Ich habe meine eigene CollectionUtils-Klasse in einem Paket, das ich projektübergreifend mit verschiedenen Dienstprogrammen verwende, die ich regelmäßig verwende, die für mich leicht zu schreiben sind und die Abhängigkeit von einigen größeren Bibliotheken vermeidet.

Hier ist meine newMap Methode:

public class CollectionUtils {
    public static Map newMap(Object... keyValuePairs) {
        Map map = new HashMap();
        if ( keyValuePairs.length % 2 == 1 ) throw new IllegalArgumentException("Must have even number of arguments");
        for ( int i=0; i<keyValuePairs.length; i+=2 ) {
            map.put(keyValuePairs[i], keyValuePairs[i + 1]);
        }
        return map;
    }
}

Verwendungszweck:

import static CollectionUtils.newMap;
// ...
Map aMap = newMap("key1", 1.23, "key2", 2.34);
Map bMap = newMap(objKey1, objVal1, objKey2, objVal2, objKey3, objVal3);
// etc...

Es werden keine Generics verwendet, aber Sie können die Karte nach Ihren Wünschen typisieren (stellen Sie sicher, dass Sie sie richtig eingeben!)

Map<String,Double> aMap = (Map<String,Double>)newMap("key1", 1.23, "key2", 2.34);
0
Jason

Die zweite Methode kann bei Bedarf geschützte Methoden aufrufen. Dies kann nützlich sein, um Klassen zu initialisieren, die nach dem Konstruieren unveränderlich sind.

0
Mark Renouf