Question Comment puis-je initialiser une carte statique?


Comment initialiser une carte statique en Java?

Première méthode: initialisateur statique
Deuxième méthode: initialiser l'instance (sous-classe anonyme) ou une autre méthode?

Quels sont les avantages et les inconvénients de chacun?

Voici un exemple illustrant deux méthodes:

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

930
2018-02-03 15:41


origine


Réponses:


L'initialiseur d'instance n'est que du sucre syntaxique dans ce cas, n'est-ce pas? Je ne vois pas pourquoi vous avez besoin d'une classe anonyme supplémentaire juste pour initialiser. Et cela ne fonctionnera pas si la classe créée est finale.

Vous pouvez également créer une carte immuable à l'aide d'un initialiseur statique:

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

945
2017-08-31 13:58



J'aime le Goyave façon d'initialiser une carte statique et immuable:

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

Comme vous pouvez le voir, c'est très concis (à cause des méthodes d'usine pratiques ImmutableMap).

Si vous souhaitez que la carte comporte plus de 5 entrées, vous ne pouvez plus utiliser ImmutableMap.of(). Au lieu de cela, essayez ImmutableMap.builder() le long de ces lignes:

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

Pour en savoir plus sur les avantages des utilitaires de collecte immuables de Guava, voir Collections immuables expliquées dans Guava Guide de l'utilisateur.

(Un sous-ensemble de) Goyave utilisé pour être appelé Google Collections. Si vous n'utilisez pas encore cette bibliothèque dans votre projet Java, fortement recommande de l'essayer! Guava est rapidement devenue l'une des librairies tierces gratuites les plus populaires et les plus utiles pour Java, les autres utilisateurs de SO sont d'accord. (Si vous êtes nouveau, il existe d'excellentes ressources d'apprentissage derrière ce lien.)


Mise à jour (2015): Pour ce qui est de Java 8Eh bien, j'utiliserais toujours l'approche de la goyave parce qu'elle est plus propre que tout le reste. Si vous ne voulez pas de dépendance à la goyave, envisagez ancienne méthode d'initialisation. Le hack avec tableau bidimensionnel et API Stream est assez moche si vous me demandez, et devient plus laide si vous avez besoin de créer une carte dont les clés et les valeurs ne sont pas du même type (comme Map<Integer, String> dans la question).

Quant à l'avenir de la goyave en général, en ce qui concerne Java 8, Louis Wasserman A dit ceci en 2014, et [mettre à jour] en 2016, il a été annoncé que Guava 21 nécessitera et supportera correctement Java 8.


Mise à jour (2016): Comme Tagir Valeev souligne, Java 9 va enfin rendre cela propre à utiliser rien que du JDK pur, en ajoutant méthodes d'usine de commodité pour les collections:

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

370
2018-02-03 21:40



J'utiliserais:

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. il évite la classe anonyme, que je considère personnellement comme un mauvais style, et évite
  2. il rend la création de la carte plus explicite
  3. il rend la carte non modifiable
  4. comme MY_MAP est constant, je le nommerais constant

160
2017-07-15 21:29



Java 5 fournit cette syntaxe plus compacte:

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

157
2018-02-03 15:44



Un avantage de la seconde méthode est que vous pouvez l'envelopper avec Collections.unmodifiableMap() pour garantir que rien ne va mettre à jour la collection plus tard:

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!

80
2017-09-14 00:44



Voici un initialiseur de carte statique d'une ligne Java 8:

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: pour initialiser un Map<Integer, String> comme dans la question, vous auriez besoin de quelque chose comme ceci:

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): Il existe une meilleure version de type mixte de i_am_zero qui utilise un flux de new SimpleEntry<>(k, v) appels. Découvrez cette réponse: https://stackoverflow.com/a/37384773/3950982


52
2017-12-29 10:07



En Java 9:

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

Voir JEP 269 pour plus de détails. JDK 9 a atteint Disponibilité générale en septembre 2017.


39
2017-12-18 23:10



Avec Collections Eclipse (anciennement GS Collections), tous les éléments suivants fonctionneront:

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

Vous pouvez également initialiser de manière statique des cartes primitives avec des collections Eclipse.

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

Remarque: Je suis un committer pour Eclipse Collections


27
2018-02-03 15:55



Je ne créerais jamais une sous-classe anonyme dans cette situation. Les initialiseurs statiques fonctionnent aussi bien, si vous souhaitez rendre la carte non modifiable, par exemple:

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

24
2018-02-03 21:33