Comment initialiseriez-vous une statique Map
en Java?
Première méthode: initialiseur statique Deuxième
méthode: initialiseur d'instance (sous-classe anonyme) ou une autre méthode?
Quels sont les avantages et les inconvénients de chacun?
Voici un exemple illustrant les deux méthodes:
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
autre choseMap.ofEntries
, consultez stackoverflow.com/a/37384773/1216775Réponses:
L'initialiseur d'instance est juste du sucre syntaxique dans ce cas, non? Je ne vois pas pourquoi vous avez besoin d'une classe anonyme supplémentaire juste pour l'initialiser. Et cela ne fonctionnera pas si la classe en cours de création est définitive.
Vous pouvez également créer une carte immuable à l'aide d'un initialiseur statique:
la source
J'aime la manière Guava d'initialiser une carte statique et immuable:
Comme vous pouvez le voir, c'est très concis (en raison des méthodes d'usine pratiques
ImmutableMap
).Si vous souhaitez que la carte comporte plus de 5 entrées, vous ne pouvez plus l'utiliser
ImmutableMap.of()
. Essayez plutôt deImmutableMap.builder()
suivre ces lignes:Pour en savoir plus sur les avantages des utilitaires de collecte immuables de Guava, voir Collections immuables expliquées dans le Guide de l'utilisateur de Guava .
(Un sous-ensemble de) La goyave s'appelait Google Collections . Si vous n'utilisez pas encore cette bibliothèque dans votre projet Java, je vous recommande fortement de l' essayer! La goyave est rapidement devenue l'une des bibliothèques tierces gratuites les plus populaires et les plus utiles pour Java, comme en conviennent les autres utilisateurs SO . (Si vous êtes novice, il existe d'excellentes ressources d'apprentissage derrière ce lien.)
Mise à jour (2015) : Quant à Java 8 , eh bien, j'utiliserais toujours l'approche Guava car elle est bien plus propre qu'autre chose. Si vous ne voulez pas de dépendance Guava, envisagez une ancienne méthode init simple . Le hack avec tableau bidimensionnel et API Stream est assez moche si vous me demandez, et devient plus moche 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 Guava en général, en ce qui concerne Java 8, Louis Wasserman l'a dit en 2014, et [ mise à jour ] en 2016, il a été annoncé que Guava 21 nécessitera et supportera correctement Java 8 .
Mise à jour (2016) : Comme le souligne Tagir Valeev , Java 9 rendra finalement cette opération propre en n'utilisant que du JDK pur, en ajoutant des méthodes d'usine pratiques pour les collections:
la source
J'utiliserais:
la source
Java 5 fournit cette syntaxe plus compacte:
la source
HashMap implements Serializable
. Puisque vous créez réellement une sous-classe de HashMap en utilisant cette "astuce", vous créez implicitement une classe Serializable. Et pour cela, vous devez fournir un serialUID.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
est défini dansAbstractMap
et fonctionne sur n'importe quelle sous-classe de Map, ce n'est donc pas un problème ici. L'opérateur de diamant est ennuyeux, mais comme mentionné, il a maintenant été résolu.Un avantage de la deuxième méthode est que vous pouvez la boucler
Collections.unmodifiableMap()
pour garantir que rien ne mettra à jour la collection plus tard:la source
Voici un initialiseur de carte statique à une ligne Java 8:
Modifier: pour initialiser un
Map<Integer, String>
comme dans la question, vous auriez besoin de quelque chose comme ceci:Edit (2): il y a une meilleure version de type mixte par i_am_zero qui utilise un flux d'
new SimpleEntry<>(k, v)
appels. Découvrez cette réponse: https://stackoverflow.com/a/37384773/3950982la source
String[][]
ne fera pas,Object[][]
est nécessaire). À mon humble avis, cette approche est laide (encore plus avec les moulages) et difficile à retenir; ne l'utiliserais pas moi-même.Map.of
en Java 9+Voir JEP 269 pour plus de détails. JDK 9 a atteint la disponibilité générale en septembre 2017.
la source
Map.ofEntries
Java 9
Nous pouvons utiliser
Map.ofEntries
, appelantMap.entry( k , v )
pour créer chaque entrée.Nous pouvons également utiliser
Map.of
comme suggéré par Tagir dans sa réponse ici, mais nous ne pouvons pas utiliser plus de 10 entréesMap.of
.Java 8 (solution soignée)
Nous pouvons créer un flux d'entrées de carte. Nous avons déjà deux implémentations
Entry
dansjava.util.AbstractMap
lesquelles sont SimpleEntry et SimpleImmutableEntry . Pour cet exemple, nous pouvons utiliser l'ancien comme:la source
new SimpleEntry<>()
chemin est beaucoup moins lisible que statiqueput()
: /Avec les collections Eclipse , tous les éléments suivants fonctionneront:
Vous pouvez également initialiser statiquement des cartes primitives avec les collections Eclipse.
Remarque: je suis un committer pour les collections Eclipse
la source
Je ne créerais jamais une sous-classe anonyme dans cette situation. Les initialiseurs statiques fonctionnent également bien, si vous souhaitez rendre la carte non modifiable par exemple:
la source
Il est peut-être intéressant de consulter Google Collections , par exemple les vidéos qu'ils ont sur leur page. Ils offrent différentes façons d'initialiser des cartes et des ensembles, ainsi que des collections immuables.
Mise à jour: cette bibliothèque s'appelle désormais Guava .
la source
J'aime les cours anonymes, car c'est facile à gérer:
la source
Si nous déclarons plus d'une constante, alors ce code sera écrit dans un bloc statique et c'est difficile à maintenir à l'avenir. Il est donc préférable d'utiliser une classe anonyme.
Et il est suggéré d'utiliser unmodifiableMap pour les constantes, sinon il ne peut pas être traité comme constant.
la source
Je pourrais fortement suggérer le style "initialisation à double accolade" plutôt que le style de bloc statique.
Quelqu'un peut dire qu'il n'aime pas les classes anonymes, les frais généraux, les performances, etc.
Mais ce que je considère plus, c'est la lisibilité et la maintenabilité du code. De ce point de vue, je pense qu'un double croisillon est un meilleur style de code plutôt qu'une méthode statique.
De plus, si vous connaissez le GC de la classe anonyme, vous pouvez toujours le convertir en HashMap normal en utilisant
new HashMap(Map map)
.Vous pouvez le faire jusqu'à ce que vous rencontriez un autre problème. Si vous le faites, vous devez utiliser un autre style de codage complet (par exemple, pas de classe statique ou d'usine).
la source
Comme d'habitude, apache-commons a la méthode appropriée MapUtils.putAll (Map, Object []) :
Par exemple, pour créer une carte de couleurs:
la source
Arrays.asMap( ... )
en langage Java simple, je pense que c'est la meilleure solution. Réinventer la roue est généralement idiot. Très léger inconvénient, c'est qu'avec les génériques, il faudra une conversion non contrôlée.SuppressWarnings( unchecked )
dans Eclipse avec une ligne commeMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
String[][]
j'obtiens "l'avertissement"! Et bien sûr, cela ne fonctionne que si votreK
etV
sont de la même classe. Je suppose que vous n'avez pas (naturellement) défini la "conversion non contrôlée" sur "Ignorer" dans votre configuration Eclipse?Voici mon préféré quand je ne veux pas (ou ne peux pas) utiliser les goyaves
ImmutableMap.of()
, ou si j'ai besoin d'un mutableMap
:Il est très compact et ignore les valeurs parasites (c'est-à-dire une clé finale sans valeur).
Usage:
la source
Si vous voulez une carte non modifiable, java 9 a enfin ajouté une méthode d'usine sympa
of
à l'Map
interface. Une méthode similaire est également ajoutée à Set, List.Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
la source
Je préfère utiliser un initialiseur statique pour éviter de générer des classes anonymes (qui n'auraient plus de raison d'être), donc je vais lister des astuces pour l'initialisation avec un initialiseur statique. Toutes les solutions / conseils répertoriés sont de type sécurisé.
Remarque: La question ne dit rien sur le fait de rendre la carte non modifiable, donc je vais laisser cela de côté, mais sachez que cela peut facilement être fait avec
Collections.unmodifiableMap(map)
.Premier conseil
La première astuce est que vous pouvez faire une référence locale à la carte et lui donner un nom COURT:
Deuxième conseil
La deuxième astuce est que vous pouvez créer une méthode d'aide pour ajouter des entrées; vous pouvez également rendre cette méthode d'assistance publique si vous souhaitez:
La méthode d'assistance ici n'est cependant pas réutilisable car elle ne peut qu'ajouter des éléments à
myMap2
. Pour le rendre réutilisable, nous pourrions faire de la carte elle-même un paramètre de la méthode d'assistance, mais alors le code d'initialisation ne serait pas plus court.Troisième conseil
La troisième astuce est que vous pouvez créer une classe d'aide de type constructeur réutilisable avec la fonctionnalité de remplissage. Il s'agit vraiment d'une classe auxiliaire simple de 10 lignes qui est de type sécurisé:
la source
La classe anonyme que vous créez fonctionne bien. Cependant, vous devez savoir qu'il s'agit d'une classe interne et, en tant que telle, elle contiendra une référence à l'instance de classe environnante. Vous constaterez donc que vous ne pouvez pas faire certaines choses avec lui (en utilisant XStream pour un). Vous obtiendrez des erreurs très étranges.
Cela dit, tant que vous le savez, cette approche est très bien. Je l'utilise la plupart du temps pour initialiser toutes sortes de collections de façon concise.
EDIT: a souligné correctement dans les commentaires qu'il s'agit d'une classe statique. Évidemment, je ne l'ai pas lu assez attentivement. Cependant mes commentaires ne s'appliquent toujours aux classes internes anonymes.
la source
Si vous voulez quelque chose de concis et de relativement sûr, vous pouvez simplement déplacer la vérification du type de compilation à l'exécution:
Cette implémentation devrait détecter toutes les erreurs:
la source
Avec Java 8, j'en suis venu à utiliser le modèle suivant:
Ce n'est pas le plus laconique et un peu détourné, mais
java.util
la source
toMap
signature incluant un fournisseur de carte pour spécifier le type de carte.Si vous n'avez besoin d'ajouter qu'une seule valeur à la carte, vous pouvez utiliser Collections.singletonMap :
la source
Vous pouvez utiliser
StickyMap
etMapEntry
depuis Cactoos :la source
On pense que votre deuxième approche (initialisation Double Brace) est un anti pattern , donc j'irais pour la première approche.
Un autre moyen simple d'initialiser une carte statique consiste à utiliser cette fonction utilitaire:
Remarque:
Java 9
vous pouvez utiliser Map.ofla source
Je n'aime pas la syntaxe d'initialisation statique et je ne suis pas convaincu des sous-classes anonymes. En général, je suis d'accord avec tous les inconvénients de l'utilisation des initialiseurs statiques et tous les inconvénients de l'utilisation des sous-classes anonymes qui ont été mentionnées dans les réponses précédentes. D'un autre côté - les pros présentés dans ces articles ne me suffisent pas. Je préfère utiliser la méthode d'initialisation statique:
la source
Je n'ai pas vu l'approche que j'utilise (et j'ai appris à aimer) publiée dans les réponses, alors voici:
Je n'aime pas utiliser les initialiseurs statiques car ils sont maladroits, et je n'aime pas les classes anonymes car cela crée une nouvelle classe pour chaque instance.
à la place, je préfère une initialisation qui ressemble à ceci:
malheureusement, ces méthodes ne font pas partie de la bibliothèque Java standard, vous devrez donc créer (ou utiliser) une bibliothèque d'utilitaires qui définit les méthodes suivantes:
(vous pouvez utiliser 'import statique' pour éviter d'avoir à préfixer le nom de la méthode)
J'ai trouvé utile de fournir des méthodes statiques similaires pour les autres collections (list, set, sortedSet, sortedMap, etc.)
Ce n'est pas aussi agréable que l'initialisation d'un objet json, mais c'est un pas dans cette direction, en ce qui concerne la lisibilité.
la source
Étant donné que Java ne prend pas en charge les littéraux de carte, les instances de carte doivent toujours être explicitement instanciées et remplies.
Heureusement, il est possible d'approximer le comportement des littéraux de carte en Java en utilisant des méthodes d'usine .
Par exemple:
Production:
C'est beaucoup plus pratique que de créer et de remplir la carte un élément à la fois.
la source
JEP 269 fournit des méthodes d'usine pratiques pour l'API Collections. Ces méthodes d'usine ne sont pas dans la version Java actuelle, qui est 8, mais sont prévues pour la version Java 9.
Car
Map
il existe deux méthodes d'usine:of
etofEntries
. À l'aide deof
, vous pouvez transmettre des paires clé / valeur alternées. Par exemple, pour créer unMap
semblable{age: 27, major: cs}
:Actuellement, il existe dix versions surchargées pour
of
, vous pouvez donc créer une carte contenant dix paires clé / valeur. Si vous n'aimez pas cette limitation ou l'alternance clé / valeurs, vous pouvez utiliserofEntries
:Les deux
of
etofEntries
renverront un immuableMap
, vous ne pouvez donc pas changer leurs éléments après la construction. Vous pouvez essayer ces fonctionnalités en utilisant JDK 9 Early Access .la source
Eh bien ... j'aime les énumérations;)
la source
J'ai lu les réponses et j'ai décidé d'écrire mon propre constructeur de cartes. N'hésitez pas à copier-coller et à en profiter.
EDIT: Dernièrement, je continue à trouver des méthodes statiques publiques
of
assez souvent et j'aime un peu ça. Je l'ai ajouté dans le code et rendu le constructeur privé, passant ainsi au modèle de méthode d'usine statique.EDIT2: Encore plus récemment, je n'aime plus la méthode statique appelée
of
, car elle semble assez mauvaise lors de l'utilisation d'importations statiques. Je l'ai renommé à lamapOf
place, ce qui le rend plus adapté aux importations statiques.la source